Browse Source

Merge branch 'master' into remove-install-scripts

Conflicts:
	frameworks/Clojure/compojure/setup.sh
	frameworks/Clojure/http-kit/setup.sh
	frameworks/Clojure/luminus/setup.sh
	frameworks/Java/play2-java/install.sh
	frameworks/Java/play2-java/setup_java.sh
	frameworks/Java/play2-java/setup_java_ebean_bonecp.sh
	frameworks/Java/play2-java/setup_java_ebean_hikaricp.sh
	frameworks/Java/play2-java/setup_java_jpa_bonecp.sh
	frameworks/Java/play2-java/setup_java_jpa_hikaricp.sh
	frameworks/Java/sabina/setup.sh
	frameworks/Java/vertx/install.sh
	frameworks/Java/vertx/setup.sh
	frameworks/JavaScript/express/install.sh
	frameworks/JavaScript/express/setup.sh
	frameworks/JavaScript/hapi/install.sh
	frameworks/JavaScript/hapi/setup.sh
	frameworks/JavaScript/nodejs/install.sh
	frameworks/JavaScript/nodejs/setup.sh
	frameworks/Lua/openresty/install.sh
	frameworks/PHP/codeigniter/install.sh
	frameworks/PHP/php-laravel/install.sh
	frameworks/Ur/urweb/install.sh
	toolset/setup/linux/frameworks/vertx.sh
	toolset/setup/linux/languages/nodejs.sh
msmith-techempower 10 years ago
parent
commit
2fd1d353be
100 changed files with 2432 additions and 1119 deletions
  1. 17 10
      .travis.yml
  2. 24 0
      frameworks/Clojure/aleph/README.md
  3. 24 0
      frameworks/Clojure/aleph/benchmark_config.json
  4. 11 0
      frameworks/Clojure/aleph/hello/.gitignore
  5. 9 0
      frameworks/Clojure/aleph/hello/project.clj
  6. 44 0
      frameworks/Clojure/aleph/hello/src/hello/handler.clj
  7. 0 0
      frameworks/Clojure/aleph/hello/src/log4j.xml
  8. 3 0
      frameworks/Clojure/aleph/install.sh
  9. 10 0
      frameworks/Clojure/aleph/setup.sh
  10. 2 0
      frameworks/Clojure/aleph/source_code
  11. 1 0
      frameworks/Clojure/compojure/benchmark_config.json
  12. 1 2
      frameworks/Clojure/compojure/hello/project.clj
  13. 137 49
      frameworks/Clojure/compojure/hello/src/hello/handler.clj
  14. 6 3
      frameworks/Clojure/http-kit/benchmark_config.json
  15. 10 2
      frameworks/Clojure/http-kit/hello/project.clj
  16. 182 47
      frameworks/Clojure/http-kit/hello/src/hello/handler.clj
  17. 3 1
      frameworks/Clojure/http-kit/setup.sh
  18. 10 28
      frameworks/Clojure/luminus/benchmark_config.json
  19. 3 4
      frameworks/Clojure/luminus/hello/.gitignore
  20. 1 0
      frameworks/Clojure/luminus/hello/Procfile
  21. 1 1
      frameworks/Clojure/luminus/hello/README.md
  22. 34 0
      frameworks/Clojure/luminus/hello/env/dev/clj/hello/repl.clj
  23. 48 39
      frameworks/Clojure/luminus/hello/project.clj
  24. 5 54
      frameworks/Clojure/luminus/hello/resources/public/css/screen.css
  25. 20 0
      frameworks/Clojure/luminus/hello/resources/sql/queries.sql
  26. 0 0
      frameworks/Clojure/luminus/hello/resources/templates/base.html
  27. 3 3
      frameworks/Clojure/luminus/hello/resources/templates/home.html
  28. 30 0
      frameworks/Clojure/luminus/hello/src/hello/core.clj
  29. 69 0
      frameworks/Clojure/luminus/hello/src/hello/db/core.clj
  30. 34 30
      frameworks/Clojure/luminus/hello/src/hello/handler.clj
  31. 37 0
      frameworks/Clojure/luminus/hello/src/hello/layout.clj
  32. 37 0
      frameworks/Clojure/luminus/hello/src/hello/middleware.clj
  33. 0 72
      frameworks/Clojure/luminus/hello/src/hello/models/db.clj
  34. 0 39
      frameworks/Clojure/luminus/hello/src/hello/models/schema.clj
  35. 46 16
      frameworks/Clojure/luminus/hello/src/hello/routes/home.clj
  36. 21 0
      frameworks/Clojure/luminus/hello/src/hello/session.clj
  37. 0 10
      frameworks/Clojure/luminus/hello/src/hello/views/layout.clj
  38. 2 4
      frameworks/Clojure/luminus/hello/test/hello/test/handler.clj
  39. 6 5
      frameworks/Clojure/luminus/setup.sh
  40. 23 14
      frameworks/Clojure/luminus/source_code
  41. 36 28
      frameworks/Clojure/pedestal/src/pedestal/service.clj
  42. 2 2
      frameworks/Java/play2-java/play2-java-ebean-bonecp/build.sbt
  43. 1 1
      frameworks/Java/play2-java/play2-java-ebean-bonecp/project/plugins.sbt
  44. 3 3
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/build.sbt
  45. 46 18
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/application.conf
  46. 1 1
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/project/plugins.sbt
  47. 3 3
      frameworks/Java/play2-java/play2-java-jpa-bonecp/build.sbt
  48. 1 1
      frameworks/Java/play2-java/play2-java-jpa-bonecp/project/plugins.sbt
  49. 4 4
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/build.sbt
  50. 47 19
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/application.conf
  51. 1 1
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/project/plugins.sbt
  52. 1 1
      frameworks/Java/play2-java/play2-java/build.sbt
  53. 1 1
      frameworks/Java/play2-java/play2-java/project/plugins.sbt
  54. 1 1
      frameworks/Java/play2-java/setup_java.sh
  55. 1 1
      frameworks/Java/play2-java/setup_java_ebean_bonecp.sh
  56. 1 1
      frameworks/Java/play2-java/setup_java_ebean_hikaricp.sh
  57. 1 1
      frameworks/Java/play2-java/setup_java_jpa_bonecp.sh
  58. 1 1
      frameworks/Java/play2-java/setup_java_jpa_hikaricp.sh
  59. 78 2
      frameworks/Java/sabina/benchmark_config.json
  60. 3 0
      frameworks/Java/sabina/jetty-mongodb.sh
  61. 3 0
      frameworks/Java/sabina/jetty.sh
  62. 6 1
      frameworks/Java/sabina/pom.xml
  63. 59 134
      frameworks/Java/sabina/src/main/java/sabina/benchmark/Application.java
  64. 0 5
      frameworks/Java/sabina/src/main/java/sabina/benchmark/Fortune.java
  65. 91 0
      frameworks/Java/sabina/src/main/java/sabina/benchmark/MongoDbRepository.java
  66. 139 0
      frameworks/Java/sabina/src/main/java/sabina/benchmark/MySqlRepository.java
  67. 22 0
      frameworks/Java/sabina/src/main/java/sabina/benchmark/Repository.java
  68. 5 2
      frameworks/Java/sabina/src/main/resources/server.properties
  69. 13 28
      frameworks/Java/sabina/src/test/java/sabina/benchmark/ApplicationTest.java
  70. 3 0
      frameworks/Java/sabina/undertow-mongodb.sh
  71. 3 0
      frameworks/Java/sabina/undertow.sh
  72. 45 8
      frameworks/Java/vertx/README.md
  73. 165 41
      frameworks/Java/vertx/WebServer.java
  74. 2 1
      frameworks/Java/vertx/app.js
  75. 2 0
      frameworks/Java/vertx/benchmark_config.json
  76. 1 1
      frameworks/Java/vertx/source_code
  77. 2 2
      frameworks/JavaScript/express/README.md
  78. 104 64
      frameworks/JavaScript/express/app.js
  79. 1 0
      frameworks/JavaScript/express/benchmark_config.json
  80. 3 0
      frameworks/JavaScript/express/install.sh
  81. 8 5
      frameworks/JavaScript/express/package.json
  82. 2 10
      frameworks/JavaScript/express/setup.sh
  83. 2 2
      frameworks/JavaScript/hapi/README.md
  84. 73 38
      frameworks/JavaScript/hapi/app.js
  85. 1 0
      frameworks/JavaScript/hapi/benchmark_config.json
  86. 6 5
      frameworks/JavaScript/hapi/package.json
  87. 1 10
      frameworks/JavaScript/hapi/setup.sh
  88. 0 0
      frameworks/JavaScript/hapi/views/fortunes.html
  89. 0 0
      frameworks/JavaScript/koa/README.md
  90. 133 0
      frameworks/JavaScript/koa/app.js
  91. 28 0
      frameworks/JavaScript/koa/benchmark_config.json
  92. 17 0
      frameworks/JavaScript/koa/package.json
  93. 9 0
      frameworks/JavaScript/koa/setup.sh
  94. 0 0
      frameworks/JavaScript/koa/source_code
  95. 21 0
      frameworks/JavaScript/koa/views/fortunes.hbs
  96. 16 5
      frameworks/JavaScript/nodejs/README.md
  97. 6 4
      frameworks/JavaScript/nodejs/benchmark_config.json
  98. 285 213
      frameworks/JavaScript/nodejs/hello.js
  99. 5 5
      frameworks/JavaScript/nodejs/package.json
  100. 3 12
      frameworks/JavaScript/nodejs/setup.sh

+ 17 - 10
.travis.yml

@@ -1,5 +1,5 @@
 language: python
 language: python
-python: 
+python:
   - "2.7"
   - "2.7"
 
 
 env:
 env:
@@ -9,12 +9,12 @@ env:
     # we end up starting ~200+ different workers. Seems that ~100 is the limit
     # we end up starting ~200+ different workers. Seems that ~100 is the limit
     # before their website starts to lag heavily
     # before their website starts to lag heavily
     #
     #
-    # Here's the bash if you need to update this. Be sure to maintain the 
+    # Here's the bash if you need to update this. Be sure to maintain the
     # lines that are currently commented out (these cannot run in Travis)
     # lines that are currently commented out (these cannot run in Travis)
-    #   cd frameworks 
+    #   cd frameworks
     #   find . -type d -depth 2 | sed 's|./|    - "TESTDIR=|' | sed 's/$/"/g'
     #   find . -type d -depth 2 | sed 's|./|    - "TESTDIR=|' | sed 's/$/"/g'
     #
     #
-    #  
+    #
     - "TESTDIR=C/lwan"
     - "TESTDIR=C/lwan"
     - "TESTDIR=C/duda"
     - "TESTDIR=C/duda"
     - "TESTDIR=C/haywire"
     - "TESTDIR=C/haywire"
@@ -34,6 +34,7 @@ env:
     - "TESTDIR=Clojure/http-kit"
     - "TESTDIR=Clojure/http-kit"
     - "TESTDIR=Clojure/luminus"
     - "TESTDIR=Clojure/luminus"
     - "TESTDIR=Clojure/pedestal"
     - "TESTDIR=Clojure/pedestal"
+    - "TESTDIR=Clojure/aleph"
     - "TESTDIR=Dart/dart"
     - "TESTDIR=Dart/dart"
     - "TESTDIR=Dart/dart-redstone"
     - "TESTDIR=Dart/dart-redstone"
     - "TESTDIR=Dart/dart-start"
     - "TESTDIR=Dart/dart-start"
@@ -82,6 +83,7 @@ env:
     - "TESTDIR=Java/wildfly-ee7"
     - "TESTDIR=Java/wildfly-ee7"
     - "TESTDIR=JavaScript/express"
     - "TESTDIR=JavaScript/express"
     - "TESTDIR=JavaScript/hapi"
     - "TESTDIR=JavaScript/hapi"
+    - "TESTDIR=JavaScript/koa"
     - "TESTDIR=JavaScript/nodejs"
     - "TESTDIR=JavaScript/nodejs"
     - "TESTDIR=JavaScript/ringojs"
     - "TESTDIR=JavaScript/ringojs"
     - "TESTDIR=JavaScript/ringojs-convenient"
     - "TESTDIR=JavaScript/ringojs-convenient"
@@ -97,6 +99,7 @@ env:
     - "TESTDIR=PHP/cakephp"
     - "TESTDIR=PHP/cakephp"
     - "TESTDIR=PHP/hhvm"
     - "TESTDIR=PHP/hhvm"
     - "TESTDIR=PHP/php"
     - "TESTDIR=PHP/php"
+    - "TESTDIR=PHP/cygnite-php-framework"
     - "TESTDIR=PHP/codeigniter"
     - "TESTDIR=PHP/codeigniter"
     - "TESTDIR=PHP/php-fatfree"
     - "TESTDIR=PHP/php-fatfree"
     - "TESTDIR=PHP/fuel"
     - "TESTDIR=PHP/fuel"
@@ -125,6 +128,8 @@ env:
     - "TESTDIR=Python/django"
     - "TESTDIR=Python/django"
     - "TESTDIR=Python/falcon"
     - "TESTDIR=Python/falcon"
     - "TESTDIR=Python/flask"
     - "TESTDIR=Python/flask"
+    - "TESTDIR=Python/historical"
+    - "TESTDIR=Python/klein"
     - "TESTDIR=Python/pyramid"
     - "TESTDIR=Python/pyramid"
     - "TESTDIR=Python/tornado"
     - "TESTDIR=Python/tornado"
     - "TESTDIR=Python/turbogears"
     - "TESTDIR=Python/turbogears"
@@ -140,6 +145,7 @@ env:
     - "TESTDIR=Ruby/rails"
     - "TESTDIR=Ruby/rails"
     - "TESTDIR=Ruby/rails-stripped"
     - "TESTDIR=Ruby/rails-stripped"
     - "TESTDIR=Ruby/sinatra"
     - "TESTDIR=Ruby/sinatra"
+    - "TESTDIR=Scala/colossus"
     - "TESTDIR=Scala/finagle"
     - "TESTDIR=Scala/finagle"
     - "TESTDIR=Scala/lift-stateless"
     - "TESTDIR=Scala/lift-stateless"
     - "TESTDIR=Scala/plain"
     - "TESTDIR=Scala/plain"
@@ -149,10 +155,11 @@ env:
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/unfiltered"
     - "TESTDIR=Scala/unfiltered"
+    - "TESTDIR=Scala/http4s"
     - "TESTDIR=Ur/urweb"
     - "TESTDIR=Ur/urweb"
 
 
 before_install:
 before_install:
-  # Need to install python modules before using 
+  # Need to install python modules before using
   # python
   # python
   - pip install -r requirements.txt
   - pip install -r requirements.txt
 
 
@@ -161,15 +168,15 @@ before_install:
   - ./toolset/run-ci.py cisetup "$TESTDIR"
   - ./toolset/run-ci.py cisetup "$TESTDIR"
 
 
 addons:
 addons:
-  postgresql: "9.3" 
+  postgresql: "9.3"
 
 
 install:
 install:
   # Install prerequisites
   # Install prerequisites
   - ./toolset/run-ci.py prereq "$TESTDIR"
   - ./toolset/run-ci.py prereq "$TESTDIR"
-  
-  # Install software for this framework  
+
+  # Install software for this framework
   - ./toolset/run-ci.py install "$TESTDIR"
   - ./toolset/run-ci.py install "$TESTDIR"
-   
-script: 
+
+script:
   # Pick one test in this directory and verify
   # Pick one test in this directory and verify
   - time ./toolset/run-ci.py verify "$TESTDIR"
   - time ./toolset/run-ci.py verify "$TESTDIR"

+ 24 - 0
frameworks/Clojure/aleph/README.md

@@ -0,0 +1,24 @@
+# Compojure Benchmarking Test
+
+This is the [Aleph](https;//github.com/ztellman/aleph) portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+
+* [JSON test source](hello/src/hello/handler.clj)
+
+## Infrastructure Software Versions
+The dependencies are documented in [project.clj](hello/project.clj),
+but the main ones are:
+
+* [Aleph 0.4.0](https://github.com/ztellman/aleph)
+* [Clojure 1.7.0-beta2](http://clojure.org/)
+* [Cheshire 5.4.0](https://github.com/dakrone/cheshire), which in turn uses [Jackson](http://jackson.codehaus.org/)
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost/json
+
+### Plaintext Test
+
+http://localhost/plaintext

+ 24 - 0
frameworks/Clojure/aleph/benchmark_config.json

@@ -0,0 +1,24 @@
+{
+  "framework": "aleph",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "aleph",
+      "language": "Clojure",
+      "orm": "Raw",
+      "platform": "Netty",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aleph",
+      "notes": "",
+      "versus": "netty"
+    }
+  }]
+}

+ 11 - 0
frameworks/Clojure/aleph/hello/.gitignore

@@ -0,0 +1,11 @@
+/target
+/lib
+/classes
+/checkouts
+pom.xml
+*.jar
+*.class
+.lein-deps-sum
+.lein-failures
+.lein-plugins
+.lein-env

+ 9 - 0
frameworks/Clojure/aleph/hello/project.clj

@@ -0,0 +1,9 @@
+(defproject hello "aleph"
+  :description "JSON/plaintext tests"
+  :dependencies [[org.clojure/clojure "1.7.0-beta2"]
+                 [clj-tuple "0.2.1"]
+                 [org.clojure/tools.cli "0.3.1"]
+                 [aleph "0.4.0"]
+                 [cheshire "5.4.0"]]
+  :main hello.handler
+  :aot :all)

+ 44 - 0
frameworks/Clojure/aleph/hello/src/hello/handler.clj

@@ -0,0 +1,44 @@
+(ns hello.handler
+  (:require
+    [byte-streams :as bs]
+    [clojure.tools.cli :as cli]
+    [aleph.http :as http]
+    [cheshire.core :as json]
+    [clj-tuple :as t])
+  (:gen-class))
+
+(def plaintext-response
+  (t/hash-map
+    :status 200
+    :headers (t/hash-map "content-type" "text/plain; charset=utf-8")
+    :body (bs/to-byte-array "Hello, World!")))
+
+(def json-response
+  (t/hash-map
+    :status 200
+    :headers (t/hash-map "content-type" "application/json")))
+
+(defn handler [{:keys [uri] :as req}]
+  (cond
+    (= "/plaintext" uri) plaintext-response
+    (= "/json" uri) (assoc json-response
+                      :body (json/encode (t/hash-map :message "Hello, World!")))
+    :else {:status 404}))
+
+;;;
+
+(defn -main [& args]
+
+  (let [[{:keys [help port]} _ banner]
+        (cli/cli args
+          ["-p" "--port" "Server port"
+           :default 8080
+           :parse-fn #(Integer/parseInt %)]
+          ["-h" "--[no-]help"])]
+
+    (when help
+      (println banner)
+      (System/exit 0))
+
+    (aleph.netty/leak-detector-level! :disabled)
+    (http/start-server handler {:port port, :executor :none})))

+ 0 - 0
frameworks/Clojure/luminus/hello/src/log4j.xml → frameworks/Clojure/aleph/hello/src/log4j.xml


+ 3 - 0
frameworks/Clojure/aleph/install.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+fw_depends java7 leiningen

+ 10 - 0
frameworks/Clojure/aleph/setup.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+source $IROOT/java7.installed
+
+source $IROOT/lein.installed
+
+cd hello
+lein clean
+lein uberjar
+java -server -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=10 -jar target/*-standalone.jar &

+ 2 - 0
frameworks/Clojure/aleph/source_code

@@ -0,0 +1,2 @@
+./aleph/hello/src/hello/handler.clj
+./aleph/hello/project.clj

+ 1 - 0
frameworks/Clojure/compojure/benchmark_config.json

@@ -6,6 +6,7 @@
       "json_url": "/compojure/json",
       "json_url": "/compojure/json",
       "db_url": "/compojure/db",
       "db_url": "/compojure/db",
       "query_url": "/compojure/db/",
       "query_url": "/compojure/db/",
+      "update_url": "/compojure/updates/",
       "fortune_url": "/compojure/fortune-hiccup",
       "fortune_url": "/compojure/fortune-hiccup",
       "plaintext_url": "/compojure/plaintext",
       "plaintext_url": "/compojure/plaintext",
       "port": 8080,
       "port": 8080,

+ 1 - 2
frameworks/Clojure/compojure/hello/project.clj

@@ -9,8 +9,7 @@
                  [mysql/mysql-connector-java "5.1.6"]
                  [mysql/mysql-connector-java "5.1.6"]
                  [org.clojure/java.jdbc "0.3.0-alpha1"]
                  [org.clojure/java.jdbc "0.3.0-alpha1"]
                  [c3p0/c3p0 "0.9.1.2"]
                  [c3p0/c3p0 "0.9.1.2"]
-                 [hiccup "1.0.4"]
-                 ]
+                 [hiccup "1.0.4"]]
   :plugins [[lein-ring "0.8.10"]]
   :plugins [[lein-ring "0.8.10"]]
   :ring {:handler hello.handler/app}
   :ring {:handler hello.handler/app}
   :profiles
   :profiles

+ 137 - 49
frameworks/Clojure/compojure/hello/src/hello/handler.clj

@@ -13,6 +13,7 @@
             [clojure.java.jdbc :as jdbc]
             [clojure.java.jdbc :as jdbc]
             [clojure.java.jdbc.sql :as sql]))
             [clojure.java.jdbc.sql :as sql]))
 
 
+
 ; Database connection
 ; Database connection
 (defdb db (mysql {:subname "//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"
 (defdb db (mysql {:subname "//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"
                   :user "benchmarkdbuser"
                   :user "benchmarkdbuser"
@@ -22,6 +23,7 @@
                   :maximum-pool-size 256
                   :maximum-pool-size 256
                   }))
                   }))
 
 
+
 ; Set up entity World and the database representation
 ; Set up entity World and the database representation
 (defentity world
 (defentity world
   (pk :id)
   (pk :id)
@@ -29,19 +31,23 @@
   (entity-fields :id :randomNumber)
   (entity-fields :id :randomNumber)
   (database db))
   (database db))
 
 
-; Query a random World record from the database
-(defn get-world []
+
+(defn get-world
+  "Query a random World record from the database"
+  []
   (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
   (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
     (select world
     (select world
             (fields :id :randomNumber)
             (fields :id :randomNumber)
             (where {:id id }))))
             (where {:id id }))))
 
 
-; Run the specified number of queries, return the results
-(defn run-queries [queries]
-   (flatten ; Make it a list of maps
-    (take
-     queries ; Number of queries to run
-     (repeatedly get-world))))
+
+(defn run-queries
+  "Run the specified number of queries, return the results"
+  [queries]
+  (flatten ; Make it a list of maps
+    (take queries ; Number of queries to run
+          (repeatedly get-world))))
+
 
 
 ; Database connection for java.jdbc "raw"
 ; Database connection for java.jdbc "raw"
 ; https://github.com/clojure/java.jdbc/blob/master/doc/clojure/java/jdbc/ConnectionPooling.md
 ; https://github.com/clojure/java.jdbc/blob/master/doc/clojure/java/jdbc/ConnectionPooling.md
@@ -52,6 +58,7 @@
    :user "benchmarkdbuser"
    :user "benchmarkdbuser"
    :password "benchmarkdbpass"})
    :password "benchmarkdbpass"})
 
 
+
 (defn pool
 (defn pool
   [spec]
   [spec]
   (let [cpds (doto (ComboPooledDataSource.)
   (let [cpds (doto (ComboPooledDataSource.)
@@ -65,35 +72,42 @@
                (.setMaxIdleTime (* 3 60 60)))]
                (.setMaxIdleTime (* 3 60 60)))]
     {:datasource cpds}))
     {:datasource cpds}))
 
 
+
 (def pooled-db (delay (pool db-spec-mysql-raw)))
 (def pooled-db (delay (pool db-spec-mysql-raw)))
 
 
+
 (defn db-raw [] @pooled-db)
 (defn db-raw [] @pooled-db)
 
 
-; Query a random World record from the database
-(defn get-world-raw []
-  (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
+
+(defn get-world-raw
+  "Query a random World record from the database"
+  []
+  (let [id (inc (rand-int 9999))]
     (jdbc/with-connection (db-raw)
     (jdbc/with-connection (db-raw)
       ; Set a naming strategy to preserve column name case
       ; Set a naming strategy to preserve column name case
       (jdbc/with-naming-strategy {:keyword identity}
       (jdbc/with-naming-strategy {:keyword identity}
         (jdbc/with-query-results rs [(str "select * from world where id = ?") id]
         (jdbc/with-query-results rs [(str "select * from world where id = ?") id]
           (doall rs))))))
           (doall rs))))))
 
 
-; Run the specified number of queries, return the results
-(defn run-queries-raw [queries]
-   (flatten ; Make it a list of maps
-    (take
-     queries ; Number of queries to run
-     (repeatedly get-world-raw))))
 
 
-(defn get-query-count [queries]
+(defn run-queries-raw
+  "Run the specified number of queries, return the results"
+  [queries]
+  (flatten ; Make it a list of maps
+    (take queries
+          (repeatedly get-world-raw))))
+
+
+(defn get-query-count
   "Parse provided string value of query count, clamping values to between 1 and 500."
   "Parse provided string value of query count, clamping values to between 1 and 500."
-  (let [q (try (Integer/parseInt queries)
+  [queries]
+  (let [n (try (Integer/parseInt queries)
                (catch Exception e 1))] ; default to 1 on parse failure
                (catch Exception e 1))] ; default to 1 on parse failure
-    (if (> q 500)
-      500 ; clamp to 500 max
-      (if (< q 1)
-        1 ; clamp to 1 min
-        q)))) ; otherwise use provided value
+    (cond
+      (< n 1) 1
+      (> n 500) 500
+      :else n)))
+
 
 
 ; Set up entity World and the database representation
 ; Set up entity World and the database representation
 (defentity fortune
 (defentity fortune
@@ -102,19 +116,27 @@
   (entity-fields :id :message)
   (entity-fields :id :message)
   (database db))
   (database db))
 
 
-(defn get-all-fortunes []
+
+(defn get-all-fortunes
   "Query all Fortune records from the database."
   "Query all Fortune records from the database."
-    (select fortune
-            (fields :id :message)))
+  []
+  (select fortune
+          (fields :id :message)))
+
 
 
-(defn get-fortunes []
+(defn get-fortunes
   "Fetch the full list of Fortunes from the database, sort them by the fortune
   "Fetch the full list of Fortunes from the database, sort them by the fortune
-message text, and then return the results."
-  (let [fortunes (conj (get-all-fortunes) {:id 0 :message "Additional fortune added at request time."} )]
+  message text, and then return the results."
+  []
+  (let [fortunes (conj (get-all-fortunes)
+                       {:id 0
+                        :message "Additional fortune added at request time."})]
     (sort-by #(:message %) fortunes)))
     (sort-by #(:message %) fortunes)))
 
 
-(defn fortunes-hiccup [fortunes]
+
+(defn fortunes-hiccup
   "Render the given fortunes to simple HTML using Hiccup."
   "Render the given fortunes to simple HTML using Hiccup."
+  [fortunes]
   (html5
   (html5
    [:head
    [:head
     [:title "Fortunes"]]
     [:title "Fortunes"]]
@@ -129,27 +151,93 @@ message text, and then return the results."
         [:td (escape-html (:message x))]])
         [:td (escape-html (:message x))]])
      ]]))
      ]]))
 
 
-(defn fortunes-enlive [fortunes]
-  "Render the given fortunes to simple HTML using Enlive."
-  "todo")
 
 
-; Define route handlers
+(defn update-and-persist
+  "Changes the :randomNumber of a number of world entities.
+  Persists the changes to sql then returns the updated entities"
+  [queries]
+  (let [results (run-queries queries)]
+    (for [w results]
+      (update-in w [:randomNumber (inc (rand-int 9999))]
+        (update world
+                (set-fields {:randomNumber (:randomNumber w)})
+                (where {:id [:id w]}))))
+    results))
+
+
+(def json-serialization
+  "Test 1: JSON serialization"
+  (response {:message "Hello, World!"}))
+
+
+(def single-query-test
+  "Test 2: Single database query"
+  (-> 1
+      (run-queries)
+      (first)
+      (response)))
+
+
+(defn multiple-query-test
+  "Test 3: Multiple database queries"
+  [queries]
+  (-> queries
+      (get-query-count)
+      (run-queries)
+      (response)))
+
+
+(def single-query-test-raw
+  "Test 2: Single database query (raw)"
+  (-> 1
+      (run-queries-raw)
+      (first)
+      (response)))
+
+
+(defn multiple-query-test-raw
+  "Test 3: Multiple database queries (raw)"
+  [queries]
+  (-> queries
+      (get-query-count)
+      (run-queries-raw)
+      (response)))
+
+
+(def fortune-test
+  "Test 4: Fortunes"
+  (response (fortunes-hiccup (get-fortunes))))
+
+
+(defn db-updates
+  "Test 5: Database updates"
+  [queries]
+  (-> queries
+      (get-query-count)
+      (update-and-persist)
+      (response)))
+
+(def plaintext
+  "Test 6: Plaintext"
+  {:status 200
+   :headers {"Content-Type" "text/plain; charset=utf-8"}
+   :body "Hello, World!"})
+
+
 (defroutes app-routes
 (defroutes app-routes
-  (GET "/" [] "Hello, World!")
-  (GET "/plaintext" []
-       {:status 200
-        :headers {"Content-Type" "text/plain; charset=utf-8"}
-        :body "Hello, World!"})
-  (GET "/json" [] (response {:message "Hello, World!"}))
-  (GET "/db" [] (response (first (run-queries 1))))
-  (GET "/db/:queries" [queries] (response (run-queries (get-query-count queries))))
-  (GET "/dbraw" [] (response (first (run-queries-raw 1))))
-  (GET "/dbraw/:queries" [queries] (response (run-queries-raw (get-query-count queries))))
-  (GET "/fortune" [] (response (get-fortunes)))
-  (GET "/fortune-hiccup" [] (fortunes-hiccup (get-fortunes)))
-  (GET "/fortune-enlive" [] (fortunes-enlive (get-fortunes)))
+  (GET "/"                 [] "Hello, World!")
+  (GET "/json"             [] json-serialization)
+  (GET "/db"               [] single-query-test)
+  (GET "/db/:queries"      [queries] (multiple-query-test queries))
+  (GET "/dbraw"            [] single-query-test-raw)
+  (GET "/dbraw/:queries"   [queries] (multiple-query-test-raw queries))
+  (GET "/fortunes"         [] (response (get-fortunes))) ; Raw json of fortunes
+  (GET "/fortune-hiccup"   [] fortune-test)
+  (GET "/updates/:queries" [queries] (db-updates queries))
+  (GET "/plaintext"        [] plaintext)
   (route/not-found "Not Found"))
   (route/not-found "Not Found"))
 
 
-; Format responses as JSON
+
 (def app
 (def app
+  "Format responses as JSON"
   (wrap-json-response app-routes))
   (wrap-json-response app-routes))

+ 6 - 3
frameworks/Clojure/http-kit/benchmark_config.json

@@ -3,9 +3,12 @@
   "tests": [{
   "tests": [{
     "default": {
     "default": {
       "setup_file": "setup",
       "setup_file": "setup",
-      "json_url": "/http-kit/json",
-      "db_url": "/http-kit/db",
-      "query_url": "/http-kit/db/",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates/",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",

+ 10 - 2
frameworks/Clojure/http-kit/hello/project.clj

@@ -6,9 +6,17 @@
                  [ring/ring-json "0.2.0"]
                  [ring/ring-json "0.2.0"]
                  [org.clojure/tools.cli "0.2.1"]
                  [org.clojure/tools.cli "0.2.1"]
                  [http-kit/dbcp "0.1.0"]
                  [http-kit/dbcp "0.1.0"]
-                 [http-kit "2.0.1"]
+                 [http-kit "2.1.18"]
                  [log4j "1.2.15" :exclusions [javax.mail/mail javax.jms/jms com.sun.jdmk/jmxtools com.sun.jmx/jmxri]]
                  [log4j "1.2.15" :exclusions [javax.mail/mail javax.jms/jms com.sun.jdmk/jmxtools com.sun.jmx/jmxri]]
-                 [mysql/mysql-connector-java "5.1.6"]]
+                 ; [ch.qos.logback/logback-classic "1.1.2" :exclusions [org.slf4j/slf4j-api]]
+                 ; [org.slf4j/jul-to-slf4j "1.7.7"]
+                 ; [org.slf4j/jcl-over-slf4j "1.7.7"]
+                 ; [org.slf4j/log4j-over-slf4j "1.7.7"]
+                 [org.clojure/data.json "0.2.5"]
+                 [org.clojure/java.jdbc "0.3.6"]
+                 [korma "0.4.0"]
+                 [mysql/mysql-connector-java "5.1.6"]
+                 [hiccup "1.0.4"]]
   :main hello.handler
   :main hello.handler
   :aot [hello.handler]
   :aot [hello.handler]
   :uberjar-name "http-kit-standalone.jar"
   :uberjar-name "http-kit-standalone.jar"

+ 182 - 47
frameworks/Clojure/http-kit/hello/src/hello/handler.clj

@@ -1,72 +1,207 @@
 (ns hello.handler
 (ns hello.handler
   (:gen-class)
   (:gen-class)
+  (:import com.mchange.v2.c3p0.ComboPooledDataSource)
   (:use compojure.core
   (:use compojure.core
         ring.middleware.json
         ring.middleware.json
         org.httpkit.server
         org.httpkit.server
         [clojure.tools.cli :only [cli]]
         [clojure.tools.cli :only [cli]]
-        ring.util.response)
+        korma.db
+        korma.core
+        hiccup.core
+        hiccup.util
+        hiccup.page)
   (:require [compojure.handler :as handler]
   (:require [compojure.handler :as handler]
-            [org.httpkit.dbcp :as db]
-            [compojure.route :as route]))
-
-;;; convert to int
-(defn to-int [s] (cond
-                  (string? s) (Integer/parseInt s)
-                  (instance? Integer s) s
-                  (instance? Long s) (.intValue ^Long s)
-                  :else 0))
-
-;; Query a random World record from the database
-(defn get-world []
-  (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
-    ; Set a naming strategy to preserve column name case
-    (clojure.java.jdbc/with-naming-strategy {:keyword identity}
-      (db/query "select * from world where id = ?" id))))
-
-;; Run the specified number of queries, return the results
-(defn run-queries [queries]
-   (flatten ; Make it a list of maps
-    (take
-     queries ; Number of queries to run
-     (repeatedly get-world))))
-
-(defn get-query-count [queries]
-  "Parse provided string value of query count, clamping values to between 1 and 500."
-  (let [q (try (Integer/parseInt queries)
+            [compojure.route :as route]
+            [ring.util.response :as ring-resp]
+            [clojure.data.json :as json]
+            [clojure.java.jdbc :as jdbc]))
+
+
+(defn parse-port [s] 
+  "Convert stringy port number int. Defaults to 8080."
+  (cond
+    (string? s) (Integer/parseInt s)
+    (instance? Integer s) s
+    (instance? Long s) (.intValue ^Long s)
+    :else 8080))
+
+
+;; MySQL connection
+(defdb mysql-db
+  (mysql {
+    :classname "com.mysql.jdbc.Driver"
+    :subprotocol "mysql"
+    :subname "//127.0.0.1:3306/hello_world"
+    :user "benchmarkdbuser"
+    :password "benchmarkdbpass"
+    ;;OPTIONAL KEYS
+    :delimiters "" ;; remove delimiters
+    :maximum-pool-size 256}))
+
+
+;; Set up entity World and the database representation
+(defentity world
+  (pk :id)
+  (table :world)
+  (entity-fields :id :randomNumber) ;; Default fields for select
+  (database mysql-db))
+
+
+(defn sanitize-queries-param
+  "Sanitizes the `queries` parameter. Caps the value between 1 and 500.
+  Invalid (stringy) values become 1"
+  [queries]
+  (let [n (try (Integer/parseInt queries)
                (catch Exception e 1))] ; default to 1 on parse failure
                (catch Exception e 1))] ; default to 1 on parse failure
-    (if (> q 500)
-      500 ; clamp to 500 max
-      (if (< q 1)
-        1 ; clamp to 1 min
-        q)))) ; otherwise use provided value
+    (cond
+      (< n 1) 1
+      (> n 500) 500
+      :else n)))
+
+
+(defn random-world
+  "Query a random World record from the database"
+  []
+  (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
+    (select world
+            (where {:id id }))))
+
+
+(defn run-queries
+  "Run query repeatedly -- Always returns an array"
+  [queries]
+  (flatten (take queries (repeatedly random-world))))
+
+; Set up entity Fortune and the database representation
+(defentity fortune
+  (pk :id)
+  (table :fortune)
+  (entity-fields :id :message)
+  (database mysql-db))
+
+
+(defn get-all-fortunes
+  "Query all Fortune records from the database."
+  []
+  (select fortune
+          (fields :id :message)))
+
+
+(defn get-fortunes
+  "Fetch the full list of Fortunes from the database, sort them by the fortune
+  message text, and then return the results."
+  []
+  (sort-by #(:message %)
+    (conj
+      (get-all-fortunes)
+      { :id 0 :message "Additional fortune added at request time." })))
+
+
+(defn fortunes-hiccup
+  "Render the given fortunes to simple HTML using Hiccup."
+  [fortunes]
+  (html5
+   [:head
+    [:title "Fortunes"]]
+   [:body
+    [:table
+     [:tr
+      [:th "id"]
+      [:th "message"]]
+     (for [x fortunes]
+       [:tr
+        [:td (:id x)]
+        [:td (escape-html (:message x))]])
+     ]]))
+
+
+(defn update-and-persist
+  "Changes the :randomNumber of a number of world entities.
+  Persists the changes to sql then returns the updated entities"
+  [queries]
+  (let [results (-> queries
+                    (sanitize-queries-param)
+                    (run-queries))]
+    (for [w results]
+      (update-in w [:randomNumber (inc (rand-int 9999))]
+        (update world
+                (set-fields {:randomNumber (:randomNumber w)})
+                (where {:id [:id w]}))))
+    results))
+
+
+(def json-serialization
+  "Test 1: JSON serialization"
+  (ring-resp/response {:message "Hello, World!"}))
+
+
+(def single-query-test
+  "Test 2: Single database query"
+  (ring-resp/response (first (run-queries 1))))
+
+
+(defn multiple-queries-test
+  "Test 3: Multiple database queries"
+  [queries]
+  (-> queries
+      (sanitize-queries-param)
+      (run-queries)
+      (ring-resp/response)
+      (ring-resp/content-type "application/json")))
+
+
+(def fortune-test
+  "Test 4: Fortunes"
+  (->
+    (get-fortunes)
+    (fortunes-hiccup)
+    (ring-resp/response)
+    (ring-resp/content-type "text/html")))
+
+
+(defn db-updates
+  "Test 5: Database updates"
+  [queries]
+  (-> queries
+      (update-and-persist)
+      (ring-resp/response)))
+
+
+(def plaintext
+  "Test 6: Plaintext"
+  (->
+    (ring-resp/response "Hello, World!")
+    (ring-resp/content-type "text/plain")))
+
 
 
 ;; Define route handlers
 ;; Define route handlers
 (defroutes app-routes
 (defroutes app-routes
-  (GET "/http-kit/" [] "Hello, World!")
-  (GET "/http-kit/json" [] (response {:message "Hello, World!"}))
-  (GET "/http-kit/db" []
-       (response (first (run-queries 1))))
-  (GET "/http-kit/db/:queries" [queries]
-       (response (run-queries (get-query-count queries))))
+  (GET "/"                 [] "Hello, World!")
+  (GET "/json"             [] json-serialization)
+  (GET "/db"               [] single-query-test)
+  (GET "/queries/:queries" [queries] (multiple-queries-test queries))
+  (GET "/fortunes"         [] fortune-test)
+  (GET "/updates/:queries" [queries] (db-updates queries))
+  (GET "/plaintext"        [] plaintext)
   (route/not-found "Not Found"))
   (route/not-found "Not Found"))
 
 
 
 
-(defn start-server [{:keys [port db-host]}]
-  (db/use-database! (str "jdbc:mysql://" db-host "/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")
-                    "benchmarkdbuser"
-                    "benchmarkdbpass")
+(defn start-server [{:keys [port]}]
   ;; Format responses as JSON
   ;; Format responses as JSON
   (let [handler (wrap-json-response app-routes)
   (let [handler (wrap-json-response app-routes)
         cpu (.availableProcessors (Runtime/getRuntime))]
         cpu (.availableProcessors (Runtime/getRuntime))]
     ;; double worker threads should increase database access performance
     ;; double worker threads should increase database access performance
-    (run-server handler {:port port :thread (* 2 cpu)})
+    (run-server handler {:port port
+                         :thread (* 2 cpu)})
     (println (str "http-kit server listens at :" port))))
     (println (str "http-kit server listens at :" port))))
 
 
+
 (defn -main [& args]
 (defn -main [& args]
   (let [[options _ banner]
   (let [[options _ banner]
         (cli args
         (cli args
-             ["-p" "--port" "Port to listen" :default 8080 :parse-fn to-int]
-             ["--db-host" "MySQL database host" :default "localhost"]
+             ["-p" "--port" "Port to listen" :default 8080 :parse-fn parse-port]
              ["--[no-]help" "Print this help"])]
              ["--[no-]help" "Print this help"])]
-    (when (:help options) (println banner) (System/exit 0))
+    (when (:help options)
+          (println banner)
+          (System/exit 0))
     (start-server options)))
     (start-server options)))

+ 3 - 1
frameworks/Clojure/http-kit/setup.sh

@@ -2,6 +2,8 @@
 
 
 fw_depends leiningen java7
 fw_depends leiningen java7
 
 
+sed -i 's|:subname "//.*:3306|:subname "//'"${DBHOST}"':3306|g' hello/src/hello/handler.clj
+
 cd hello
 cd hello
 lein clean
 lein clean
 lein deps
 lein deps
@@ -10,4 +12,4 @@ rm -rf target
 lein uberjar
 lein uberjar
 # -server is much faster
 # -server is much faster
 # 'lein run' passes '-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' which make it starts fast, but runs slow
 # 'lein run' passes '-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' which make it starts fast, but runs slow
-java -server -jar target/http-kit-standalone.jar --db-host ${DBHOST} &
+java -server -jar target/http-kit-standalone.jar  &

+ 10 - 28
frameworks/Clojure/luminus/benchmark_config.json

@@ -3,44 +3,26 @@
   "tests": [{
   "tests": [{
     "default": {
     "default": {
       "setup_file": "setup",
       "setup_file": "setup",
-      "json_url": "/luminus/json",
-      "db_url": "/luminus/db",
-      "query_url": "/luminus/db/",
-      "fortune_url": "/luminus/fortune",
-      "plaintext_url": "/luminus/plaintext",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates/",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MySQL",
-      "framework": "luminus",
-      "language": "Clojure",
-      "orm": "Full",
-      "platform": "Servlet",
-      "webserver": "Resin",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "luminus",
-      "notes": "",
-      "versus": "servlet"
-    },
-    "raw": {
-      "setup_file": "setup",
-      "db_url": "/luminus/dbraw",
-      "query_url": "/luminus/dbraw/",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Platform",
       "database": "MySQL",
       "database": "MySQL",
       "framework": "luminus",
       "framework": "luminus",
       "language": "Clojure",
       "language": "Clojure",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Servlet",
-      "webserver": "Resin",
+      "platform": "http-kit",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "luminus",
       "display_name": "luminus",
       "notes": "",
       "notes": "",
-      "versus": "servlet"
+      "versus": ""
     }
     }
   }]
   }]
 }
 }

+ 3 - 4
frameworks/Clojure/luminus/hello/.gitignore

@@ -5,7 +5,6 @@
 pom.xml
 pom.xml
 *.jar
 *.jar
 *.class
 *.class
-.lein-deps-sum
-.lein-failures
-.lein-plugins
-.lein-env
+/.lein-*
+/.env
+*.log

+ 1 - 0
frameworks/Clojure/luminus/hello/Procfile

@@ -0,0 +1 @@
+web: java $JVM_OPTS -cp target/hello.jar clojure.main -m hello.core

+ 1 - 1
frameworks/Clojure/luminus/hello/README.md

@@ -16,4 +16,4 @@ To start a web server for the application, run:
 
 
 ## License
 ## License
 
 
-Copyright © 2013 FIXME
+Copyright © 2015 FIXME

+ 34 - 0
frameworks/Clojure/luminus/hello/env/dev/clj/hello/repl.clj

@@ -0,0 +1,34 @@
+(ns hello.repl
+  (:use hello.handler
+    ring.server.standalone
+    [ring.middleware file-info file]))
+
+(defonce server (atom nil))
+
+(defn get-handler []
+  ;; #'app expands to (var app) so that when we reload our code,
+  ;; the server is forced to re-resolve the symbol in the var
+  ;; rather than having its own copy. When the root binding
+  ;; changes, the server picks it up without having to restart.
+  (-> #'app
+      ; Makes static assets in $PROJECT_DIR/resources/public/ available.
+      (wrap-file "resources")
+      ; Content-Type, Content-Length, and Last Modified headers for files in body
+      (wrap-file-info)))
+
+(defn start-server
+  "used for starting the server in development mode from REPL"
+  [& [port]]
+  (let [port (if port (Integer/parseInt port) 3000)]
+    (reset! server
+            (serve (get-handler)
+                   {:port port
+                    :init init
+                    :auto-reload? true
+                    :destroy destroy
+                    :join? false}))
+    (println (str "You can view the site at http://localhost:" port))))
+
+(defn stop-server []
+  (.stop @server)
+  (reset! server nil))

+ 48 - 39
frameworks/Clojure/luminus/hello/project.clj

@@ -1,40 +1,49 @@
-(defproject
-  hello
-  "luminus"
-  :dependencies
-  [[org.clojure/clojure "1.5.1"]
-   [lib-noir "0.8.2"]
-   [compojure "1.1.6"]
-   [ring-server "0.3.1"]
-   [selmer "0.5.7"]
-   [com.taoensso/timbre "2.7.1"]
-   [com.postspectacular/rotor "0.1.0"]
-   [com.taoensso/tower "2.0.2"]
-   [mysql/mysql-connector-java "5.1.28"]
-   [korma "0.3.0-RC6"]
-   [c3p0/c3p0 "0.9.1.2"]
-   [log4j
-    "1.2.17"
-    :exclusions
-    [javax.mail/mail
-     javax.jms/jms
-     com.sun.jdmk/jmxtools
-     com.sun.jmx/jmxri]]]
-  :ring
-  {:handler hello.handler/app,
-   :init hello.handler/init,
-   :destroy hello.handler/destroy}
+(defproject hello "luminus"
+
+  :description "Luminus framework benchmarks"
+  :url "https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Clojure/luminus"
+
+  :dependencies [[org.clojure/clojure "1.6.0"]
+                 [ring-server "0.4.0"]
+                 [selmer "0.8.2"]
+                 [com.taoensso/timbre "3.4.0"]
+                 [com.taoensso/tower "3.0.2"]
+                 [markdown-clj "0.9.65"]
+                 [environ "1.0.0"]
+                 [im.chit/cronj "1.4.3"]
+                 [compojure "1.3.3"]
+                 [ring/ring-defaults "0.1.4"]
+                 [ring/ring-session-timeout "0.1.0"]
+                 [ring-middleware-format "0.5.0"]
+                 [noir-exception "0.2.3"]
+                 [bouncer "0.3.2"]
+                 [prone "0.8.1"]
+                 [org.clojure/tools.nrepl "0.2.8"]
+                 [yesql "0.5.0-rc2"]
+                 [mysql/mysql-connector-java "5.1.6"]
+                 [c3p0/c3p0 "0.9.1.2"]
+                 [http-kit "2.1.19"]
+                 [org.clojure/tools.cli "0.2.1"]]
+
+  :min-lein-version "2.0.0"
+  :uberjar-name "hello.jar"
+  :jvm-opts ["-server"]
+
+  :main hello.core
+
+  :plugins [[lein-ring "0.9.1"]
+            [lein-environ "1.0.0"]]
+
   :profiles
   :profiles
-  {:uberjar {:aot :all}
-   :production
-   {:ring
-    {:open-browser? false, :stacktraces? false, :auto-reload? false}},
-   :dev
-   {:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.2.2"]]}}
-  :url
-  "http://example.com/FIXME"
-  :plugins
-  [[lein-ring "0.8.10"]]
-  :description
-  "FIXME: write description"
-  :min-lein-version "2.0.0")
+  {:uberjar {:omit-source true
+             :env {:production true}
+             :aot :all}
+   :dev {:dependencies [[ring-mock "0.1.5"]
+                        [ring/ring-devel "1.3.2"]
+                        [pjstadig/humane-test-output "0.7.0"]]
+         :source-paths ["env/dev/clj"]
+
+         :repl-options {:init-ns hello.repl}
+         :injections [(require 'pjstadig.humane-test-output)
+                      (pjstadig.humane-test-output/activate!)]
+         :env {:dev true}}})

+ 5 - 54
frameworks/Clojure/luminus/hello/resources/public/css/screen.css

@@ -1,55 +1,6 @@
-body {	
-    background-color: #fff;
-    color: #333;
-    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+html,
+body {
+	font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+    height: 100%;
+    padding-top: 40px;
 }
 }
-
-#register {
-    float: left;
-    display: inline-block;
-}
-
-h1 {
-    -webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-	background-color: #E9E9E9;
-	border: 1px solid #CFCFCF;
-	border-radius: 4px;
-}
-
-.nav li {
-    float: left;
-    list-style: none;
-    padding: 5px;
-}
-
-.navbar {
-    text-align: right;
-    padding: 5px;
-    margin-bottom: 10px;
-}
-
-#container {
-    height: auto;
-    margin-bottom: 10px;
-}
-
-#footer {    
-    width: 95%;
-    margin-left: 1%;
-    margin-right: 1%;
-    position:absolute;
-    bottom:0;
-    font-size: 11px;
-    font-weight: bold;
-    height: auto;    
-    text-align: center;
-    
-	padding-top: 10px;
-	
-	-webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-	background-color: #E9E9E9;
-	border: 1px solid #CFCFCF;
-	border-radius: 4px;
-}

+ 20 - 0
frameworks/Clojure/luminus/hello/resources/sql/queries.sql

@@ -0,0 +1,20 @@
+--name: get-world
+-- Query a World record from the database
+SELECT * FROM world
+WHERE id = :id
+
+--name: get-all-fortunes
+-- select all records from the fortune table
+SELECT * FROM fortune
+
+--name: update-world<!
+-- update an existing world record
+UPDATE world
+SET randomNumber = :randomNumber
+WHERE id = :id
+
+--name: get-all-fortunes
+-- query all fortune records
+SELECT id, message FROM fortune
+
+

+ 0 - 0
frameworks/Clojure/luminus/hello/src/hello/views/templates/base.html → frameworks/Clojure/luminus/hello/resources/templates/base.html


+ 3 - 3
frameworks/Clojure/luminus/hello/src/hello/views/templates/home.html → frameworks/Clojure/luminus/hello/resources/templates/home.html

@@ -1,4 +1,4 @@
-{% extends "hello/views/templates/base.html" %}
+{% extends "base.html" %}
 
 
 {% block content %}
 {% block content %}
 
 
@@ -13,6 +13,6 @@
     <td>{{message.message}}</td>
     <td>{{message.message}}</td>
   </tr>
   </tr>
   {% endfor %}
   {% endfor %}
-</table>  
-  
+</table>
+
 {% endblock %}
 {% endblock %}

+ 30 - 0
frameworks/Clojure/luminus/hello/src/hello/core.clj

@@ -0,0 +1,30 @@
+(ns hello.core
+  (:require [hello.handler :refer [app init]]
+            [clojure.tools.cli :refer [cli]]
+            [org.httpkit.server :refer [run-server]])
+  (:gen-class))
+
+(defn parse-port [s]
+  "Convert stringy port number int. Defaults to 8080."
+  (cond
+    (string? s) (Integer/parseInt s)
+    (instance? Integer s) s
+    (instance? Long s) (.intValue ^Long s)
+    :else 8080))
+
+(defn start-server [{:keys [port]}]
+  (let [cpu (.availableProcessors (Runtime/getRuntime))]
+    ;; double worker threads should increase database access performance
+    (init)
+    (run-server app {:port port :thread (* 2 cpu)})
+    (println (str "http-kit server listens at :" port))))
+
+(defn -main [& args]
+  (let [[options _ banner]
+        (cli args
+             ["-p" "--port" "Port to listen" :default 8080 :parse-fn parse-port]
+             ["--[no-]help" "Print this help"])]
+    (when (:help options)
+          (println banner)
+          (System/exit 0))
+    (start-server options)))

+ 69 - 0
frameworks/Clojure/luminus/hello/src/hello/db/core.clj

@@ -0,0 +1,69 @@
+(ns hello.db.core
+  (:require
+    [yesql.core :refer [defqueries]])
+  (:import com.mchange.v2.c3p0.ComboPooledDataSource))
+
+(def db-spec
+  {:classname "com.mysql.jdbc.Driver"
+   :subprotocol "mysql"
+   :subname "//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"
+   :user "benchmarkdbuser"
+   :password "benchmarkdbpass"})
+
+(defn pool
+  [spec]
+  {:datasource
+   (doto (ComboPooledDataSource.)
+     (.setDriverClass (:classname spec))
+     (.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
+     (.setUser (:user spec))
+     (.setPassword (:password spec))
+     ;; expire excess connections after 30 minutes of inactivity:
+     (.setMaxIdleTimeExcessConnections (* 30 60))
+     ;; expire connections after 3 hours of inactivity:
+     (.setMaxIdleTime (* 3 60 60)))})
+
+(defonce db (atom nil))
+
+(defn connect! []
+  (reset! db (pool db-spec)))
+
+(defqueries "sql/queries.sql")
+
+(defn get-world-random
+  "Query a random World record between 1 and 10,000 from the database"
+  []
+  (get-world {:id (inc (rand-int 9999))} {:connection @db}))
+
+(defn get-query-count [queries]
+  "Parse provided string value of query count, clamping values to between 1 and 500."
+  (let [n (try (Integer/parseInt queries)
+               (catch Exception e 1))] ; default to 1 on parse failure
+    (cond
+      (< n 1) 1
+      (> n 500) 500
+      :else n)))
+
+(defn run-queries
+  "Run the specified number of queries, return the results"
+  [queries]
+  (flatten (repeatedly (get-query-count queries) get-world-random)))
+
+(defn get-fortunes []
+   "Fetch the full list of Fortunes from the database, sort them by the fortune
+  message text, and then return the results."
+  (sort-by
+   :message
+   (conj (get-all-fortunes {} {:connection @db})
+         {:id 0 :message "Additional fortune added at request time."})))
+
+(defn update-and-persist
+  "Changes the :randomNumber of a number of world entities.
+  Persists the changes to sql then returns the updated entities"
+  [queries]
+  (for [world (-> queries run-queries)]
+    (let [updated-world (assoc world :randomNumber (inc (rand-int 9999)))]
+      (update-world<! updated-world {:connection @db})
+      updated-world)))
+
+

+ 34 - 30
frameworks/Clojure/luminus/hello/src/hello/handler.clj

@@ -1,17 +1,20 @@
 (ns hello.handler
 (ns hello.handler
-  (:use hello.routes.home
-        compojure.core)
-  (:require [noir.util.middleware :as middleware]
+  (:require [compojure.core :refer [defroutes routes]]
+            [hello.routes.home :refer [home-routes]]
+            [hello.db.core :as db]
+            [hello.middleware
+             :refer [development-middleware production-middleware]]
+            [hello.session :as session]
             [compojure.route :as route]
             [compojure.route :as route]
             [taoensso.timbre :as timbre]
             [taoensso.timbre :as timbre]
-            [taoensso.timbre.appenders.rotor :as rotor]))
+            [taoensso.timbre.appenders.rotor :as rotor]
+            [selmer.parser :as parser]
+            [environ.core :refer [env]]
+            [cronj.core :as cronj]))
 
 
-(defroutes app-routes
-  (route/resources "/")
-  (route/not-found "Not Found"))
-
-(defn destroy []
-  (timbre/info "picture-gallery is shutting down"))
+(defroutes base-routes
+           (route/resources "/")
+           (route/not-found "Not Found"))
 
 
 (defn init
 (defn init
   "init will be called once when
   "init will be called once when
@@ -21,33 +24,34 @@
   []
   []
   (timbre/set-config!
   (timbre/set-config!
     [:appenders :rotor]
     [:appenders :rotor]
-    {:min-level :info
-     :enabled? true
-     :async? false ; should be always false for rotor
+    {:min-level             :info
+     :enabled?              true
+     :async?                false ; should be always false for rotor
      :max-message-per-msecs nil
      :max-message-per-msecs nil
-     :fn rotor/appender-fn})
+     :fn                    rotor/appender-fn})
 
 
   (timbre/set-config!
   (timbre/set-config!
     [:shared-appender-config :rotor]
     [:shared-appender-config :rotor]
-    {:path "{{sanitized}}.log" :max-size (* 512 1024) :backlog 10})
+    {:path "hello.log" :max-size (* 512 1024) :backlog 10})
 
 
-  (timbre/info "hello started successfully"))
+  (if (env :dev) (parser/cache-off!))
+  (db/connect!)
+  ;;start the expired session cleanup job
+  (cronj/start! session/cleanup-job)
+  (timbre/info "\n-=[ hello started successfully"
+               (when (env :dev) "using the development profile") "]=-"))
 
 
 (defn destroy
 (defn destroy
   "destroy will be called when your application
   "destroy will be called when your application
    shuts down, put any clean up code here"
    shuts down, put any clean up code here"
   []
   []
-  (timbre/info "hello is shutting down..."))
-
-
-(def app (middleware/app-handler
-           ;; add your application routes here
-           [home-routes app-routes]
-           ;; add custom middleware here
-           :middleware []
-           ;; add access rules here
-           :access-rules []
-           ;; serialize/deserialize the following data formats
-           ;; available formats:
-           ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html
-           :formats [:json-kw :edn]))
+  (timbre/info "hello is shutting down...")
+  (cronj/shutdown! session/cleanup-job)
+  (timbre/info "shutdown complete!"))
+
+(def app
+  (-> (routes
+        home-routes
+        base-routes)
+      development-middleware
+      production-middleware))

+ 37 - 0
frameworks/Clojure/luminus/hello/src/hello/layout.clj

@@ -0,0 +1,37 @@
+(ns hello.layout
+  (:require [selmer.parser :as parser]
+            [selmer.filters :as filters]
+            [markdown.core :refer [md-to-html-string]]
+            [ring.util.response :refer [content-type response]]
+            [compojure.response :refer [Renderable]]
+            [ring.util.anti-forgery :refer [anti-forgery-field]]
+            [ring.middleware.anti-forgery :refer [*anti-forgery-token*]]
+            [environ.core :refer [env]]))
+
+(parser/set-resource-path!  (clojure.java.io/resource "templates"))
+
+(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
+(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)]))
+
+(deftype RenderableTemplate [template params]
+  Renderable
+  (render [this request]
+    (content-type
+      (->> (assoc params
+                  :page template
+                  :dev (env :dev)
+                  :csrf-token *anti-forgery-token*
+                  :servlet-context
+                  (if-let [context (:servlet-context request)]
+                    ;; If we're not inside a serlvet environment (for
+                    ;; example when using mock requests), then
+                    ;; .getContextPath might not exist
+                    (try (.getContextPath context)
+                         (catch IllegalArgumentException _ context))))
+        (parser/render-file (str template))
+        response)
+      "text/html; charset=utf-8")))
+
+(defn render [template & [params]]
+  (RenderableTemplate. template params))
+

+ 37 - 0
frameworks/Clojure/luminus/hello/src/hello/middleware.clj

@@ -0,0 +1,37 @@
+(ns hello.middleware
+  (:require [hello.session :as session]
+            [taoensso.timbre :as timbre]
+            [environ.core :refer [env]]
+            [selmer.middleware :refer [wrap-error-page]]
+            [prone.middleware :refer [wrap-exceptions]]
+            [ring.util.response :refer [redirect]]
+            [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
+            [ring.middleware.session-timeout :refer [wrap-idle-session-timeout]]
+            [noir-exception.core :refer [wrap-internal-error]]
+            [ring.middleware.session.memory :refer [memory-store]]
+            [ring.middleware.format :refer [wrap-restful-format]]
+            
+            ))
+
+(defn log-request [handler]
+  (fn [req]
+    (timbre/debug req)
+    (handler req)))
+
+(defn development-middleware [handler]
+  (if (env :dev)
+    (-> handler
+        wrap-error-page
+        wrap-exceptions)
+    handler))
+
+(defn production-middleware [handler]
+  (-> handler
+      
+      (wrap-restful-format :formats [:json-kw :edn :transit-json :transit-msgpack])
+      (wrap-idle-session-timeout
+        {:timeout (* 60 30)
+         :timeout-response (redirect "/")})
+      (wrap-defaults
+        (assoc-in site-defaults [:session :store] (memory-store session/mem)))
+      (wrap-internal-error :log #(timbre/error %))))

+ 0 - 72
frameworks/Clojure/luminus/hello/src/hello/models/db.clj

@@ -1,72 +0,0 @@
-(ns hello.models.db
-  (:use korma.core
-         hello.models.schema)
-  (:require [clojure.java.jdbc :as jdbc]))
-
-
-
-; Set up entity World and the database representation
-(defentity world
-  (pk :id)
-  (table :world)
-  (entity-fields :id :randomNumber)
-  (database db))
-
-; Query a random World record from the database
-(defn get-world []
-  (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
-    (select world
-            (fields :id :randomNumber)
-            (where {:id id }))))
-
-; Query a random World record from the database
-(defn get-world-raw []
-  (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
-    (jdbc/with-connection (db-raw)
-      ; Set a naming strategy to preserve column name case
-      (jdbc/with-naming-strategy {:keyword identity}
-        (jdbc/with-query-results rs [(str "select * from world where id = ?") id]
-          (doall rs))))))
-
-; Run the specified number of queries, return the results
-(defn run-queries [queries]
-   (flatten ; Make it a list of maps
-    (take
-     queries ; Number of queries to run
-     (repeatedly get-world))))
-
-; Run the specified number of queries, return the results
-(defn run-queries-raw [queries]
-   (flatten ; Make it a list of maps
-    (take
-     queries ; Number of queries to run
-     (repeatedly get-world-raw))))
-
-(defn get-query-count [queries]
-  "Parse provided string value of query count, clamping values to between 1 and 500."
-  (let [q (try (Integer/parseInt queries)
-               (catch Exception e 1))] ; default to 1 on parse failure
-    (if (> q 500)
-      500 ; clamp to 500 max
-      (if (< q 1)
-        1 ; clamp to 1 min
-        q)))) ; otherwise use provided value
-
-
-; Set up entity World and the database representation
-(defentity fortune
-  (pk :id)
-  (table :fortune)
-  (entity-fields :id :message)
-  (database db))
-
-(defn get-all-fortunes []
-  "Query all Fortune records from the database."
-    (select fortune
-            (fields :id :message)))
-
-(defn get-fortunes []
-  "Fetch the full list of Fortunes from the database, sort them by the fortune
-   message text, and then return the results."
-  (let [fortunes (conj (get-all-fortunes) {:id 0 :message "Additional fortune added at request time."} )]
-    (sort-by :message fortunes)))

+ 0 - 39
frameworks/Clojure/luminus/hello/src/hello/models/schema.clj

@@ -1,39 +0,0 @@
-(ns hello.models.schema
-  (:require [korma.db :refer [defdb mysql]]
-            [clojure.java.jdbc :as sql])
-  (:import com.mchange.v2.c3p0.ComboPooledDataSource))
-
-; Database connection for java.jdbc "raw"
-; https://github.com/clojure/java.jdbc/blob/master/doc/clojure/java/jdbc/ConnectionPooling.md
-(def db-spec-mysql-raw
-  {:classname "com.mysql.jdbc.Driver"
-   :subprotocol "mysql"
-   :subname "//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"
-   :user "benchmarkdbuser"
-   :password "benchmarkdbpass"})
-
-; Database connection
-(defdb db (mysql {:subname "//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"
-                  :user "benchmarkdbuser"
-                  :password "benchmarkdbpass"
-                  ;;OPTIONAL KEYS
-                  :delimiters "" ;; remove delimiters
-                  :maximum-pool-size 256
-                  }))
-                  
-(defn pool
-  [spec]
-  (let [cpds (doto (ComboPooledDataSource.)
-               (.setDriverClass (:classname spec))
-               (.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
-               (.setUser (:user spec))
-               (.setPassword (:password spec))
-               ;; expire excess connections after 30 minutes of inactivity:
-               (.setMaxIdleTimeExcessConnections (* 30 60))
-               ;; expire connections after 3 hours of inactivity:
-               (.setMaxIdleTime (* 3 60 60)))]
-    {:datasource cpds}))
-
-(def pooled-db (delay (pool db-spec-mysql-raw)))
-
-(defn db-raw [] @pooled-db)

+ 46 - 16
frameworks/Clojure/luminus/hello/src/hello/routes/home.clj

@@ -1,19 +1,49 @@
 (ns hello.routes.home
 (ns hello.routes.home
-  (:use compojure.core
-        hello.models.db)
-  (:require [hello.views.layout :as layout]
-            [noir.response :as response]))
+  (:require [hello.layout :as layout]
+            [hello.db.core :as db]
+            [compojure.core :refer [defroutes GET]]
+            [ring.util.response :refer [response]]
+            [clojure.java.io :as io]))
 
 
-(defroutes home-routes
-  (GET "/" [] "Hello, World!")
-  (GET "/plaintext" []
-       {:status 200
-        :headers {"Content-Type" "text/plain; charset=utf-8"}
-        :body "Hello, World!"})
-  (GET "/json" [] (response/json {:message "Hello, World!"}))
-  (GET "/db" [] (response/json (first (run-queries 1))))
-  (GET "/db/:queries" [queries] (response/json (run-queries (get-query-count queries))))
-  (GET "/dbraw" [] (response/json (first (run-queries-raw 1))))
-  (GET "/dbraw/:queries" [queries] (response/json (run-queries-raw (get-query-count queries))))  
-  (GET "/fortune" [] (layout/render "home.html" {:messages (get-fortunes)})))
+(def json-serialization
+  "Test 1: JSON serialization"
+  (response {:message "Hello, World!"}))
+
+(defn single-query-test
+  "Test 2: Single database query"
+  []
+  (-> 1 db/run-queries first response))
+
+(defn multiple-query-test
+  "Test 3: Multiple database query"
+  [queries]
+  (-> queries
+      db/run-queries
+      response))
+
+(defn fortunes
+  "Test 4: Fortunes"
+  []
+  (layout/render "home.html" {:messages (db/get-fortunes)}))
 
 
+(defn db-update
+  "Test 5: Database updates"
+  [queries]
+  (-> queries
+      db/update-and-persist
+      response))
+
+(def plaintext
+  "Test 6: Plaintext"
+  {:status 200
+   :headers {"Content-Type" "text/plain"}
+   :body "Hello, World!"})
+
+(defroutes home-routes
+  (GET "/"                 [] "Hello, World!")
+  (GET "/plaintext"        [] plaintext)
+  (GET "/json"             [] json-serialization)
+  (GET "/db"               [] (single-query-test))
+  (GET "/queries/:queries" [queries] (multiple-query-test queries))
+  (GET "/fortunes"         [] (fortunes))
+  (GET "/updates/:queries"  [queries] (db-update queries)))

+ 21 - 0
frameworks/Clojure/luminus/hello/src/hello/session.clj

@@ -0,0 +1,21 @@
+(ns hello.session
+  (:require [cronj.core :refer [cronj]]))
+
+(defonce mem (atom {}))
+
+(defn- current-time []
+  (quot (System/currentTimeMillis) 1000))
+
+(defn- expired? [[id session]]
+  (pos? (- (:ring.middleware.session-timeout/idle-timeout session) (current-time))))
+
+(defn clear-expired-sessions []
+  (clojure.core/swap! mem #(->> % (filter expired?) (into {}))))
+
+(def cleanup-job
+  (cronj
+    :entries
+    [{:id "session-cleanup"
+      :handler (fn [_ _] (clear-expired-sessions))
+      :schedule "* /30 * * * * *"
+      :opts {}}]))

+ 0 - 10
frameworks/Clojure/luminus/hello/src/hello/views/layout.clj

@@ -1,10 +0,0 @@
-(ns hello.views.layout
-  (:use noir.request)
-  (:require [selmer.parser :as parser]))
-
-(def template-path "hello/views/templates/")
-
-(defn render [template & [params]]
-  (parser/render-file (str template-path template)
-                      (assoc params :context (:context *request*))))
-

+ 2 - 4
frameworks/Clojure/luminus/hello/test/hello/test/handler.clj

@@ -6,10 +6,8 @@
 (deftest test-app
 (deftest test-app
   (testing "main route"
   (testing "main route"
     (let [response (app (request :get "/"))]
     (let [response (app (request :get "/"))]
-      (is (= (:status response) 200))
-      (is (= (:body response)
-             "Hello, World!"))))
+      (is (= 200 (:status response)))))
 
 
   (testing "not-found route"
   (testing "not-found route"
     (let [response (app (request :get "/invalid"))]
     (let [response (app (request :get "/invalid"))]
-      (is (= (:status response) 404)))))
+      (is (= 404 (:status response))))))

+ 6 - 5
frameworks/Clojure/luminus/setup.sh

@@ -2,11 +2,12 @@
 
 
 fw_depends java7 resin leiningen 
 fw_depends java7 resin leiningen 
 
 
-sed -i 's|:subname "//.*:3306|:subname "//'"${DBHOST}"':3306|g' hello/src/hello/models/schema.clj
+# Update db host in the source file
+sed -i 's|:subname "//.*:3306|:subname "//'"${DBHOST}"':3306|g' hello/src/hello/db/core.clj
 
 
 cd hello
 cd hello
 lein clean
 lein clean
-lein ring uberwar
-rm -rf $RESIN_HOME/webapps/*
-cp target/hello-luminus-standalone.war $RESIN_HOME/webapps/luminus.war
-resinctl start
+rm -rf target
+# pack all dependencies into a single jar: target/http-kit-standalone.jar
+lein uberjar
+java -server -jar target/hello.jar  &

+ 23 - 14
frameworks/Clojure/luminus/source_code

@@ -1,23 +1,32 @@
+./luminus/hello/Procfile
+./luminus/hello/README.md
 ./luminus/hello/project.clj
 ./luminus/hello/project.clj
-./luminus/hello/src/
-./luminus/hello/src/log4j.xml
-./luminus/hello/src/hello
-./luminus/hello/src/hello/routes
-./luminus/hello/src/hello/routes/home.clj
-./luminus/hello/src/hello/views
-./luminus/hello/src/hello/views/templates
-./luminus/hello/src/hello/views/templates/base.html
-./luminus/hello/src/hello/views/templates/home.html
-./luminus/hello/src/hello/views/layout.clj
-./luminus/hello/src/hello/handler.clj
-./luminus/hello/src/hello/models
-./luminus/hello/src/hello/models/schema.clj
-./luminus/hello/src/hello/models/db.clj
+./luminus/hello/env/
+./luminus/hello/env/dev/clj
+./luminus/hello/env/dev/clj/hello
+./luminus/hello/env/dev/clj/hello/repl.clj
 ./luminus/hello/resources/
 ./luminus/hello/resources/
 ./luminus/hello/resources/public
 ./luminus/hello/resources/public
 ./luminus/hello/resources/public/css
 ./luminus/hello/resources/public/css
 ./luminus/hello/resources/public/css/screen.css
 ./luminus/hello/resources/public/css/screen.css
+./luminus/hello/resources/sql
+./luminus/hello/resources/sql/queries.sql
+./luminus/hello//resources/templates
+./luminus/hello//resources/templates/base.html
+./luminus/hello//resources/templates/home.html
+./luminus/hello/src/
+./luminus/hello/src/hello
+./luminus/hello/src/hello/core.clj
+./luminus/hello/src/hello/handler.clj
+./luminus/hello/src/hello/layout.clj
+./luminus/hello/src/hello/middleware.clj
+./luminus/hello/src/hello/session.clj
+./luminus/hello/src/hello/db
+./luminus/hello/src/hello/db/core.clj
+./luminus/hello/src/hello/routes
+./luminus/hello/src/hello/routes/home.clj
 ./luminus/hello/test/
 ./luminus/hello/test/
 ./luminus/hello/test/hello
 ./luminus/hello/test/hello
 ./luminus/hello/test/hello/test
 ./luminus/hello/test/hello/test
 ./luminus/hello/test/hello/test/handler.clj
 ./luminus/hello/test/hello/test/handler.clj
+

+ 36 - 28
frameworks/Clojure/pedestal/src/pedestal/service.clj

@@ -41,11 +41,12 @@
   (database mysql-db))
   (database mysql-db))
 
 
 
 
-(defn random-world []
+(defn random-world
   "Query a random World record from the database"
   "Query a random World record from the database"
+  []
   (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
   (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
     (select world
     (select world
-      (where {:id id }))))
+            (where {:id id }))))
 
 
 
 
 (defn run-queries
 (defn run-queries
@@ -62,26 +63,27 @@
 
 
 (defn sanitizeQueriesParam
 (defn sanitizeQueriesParam
   "Sanitizes the `queries` parameter. Caps the value between 1 and 500.
   "Sanitizes the `queries` parameter. Caps the value between 1 and 500.
-Invalid (stringy) values become 1"
+  Invalid (stringy) values become 1"
   [request]
   [request]
-  (let [queries (-> request :params :queries)]
-    (let [n
-      (if (= (re-find #"\A-?\d+" queries) nil)
-        1
-        (Integer/parseInt queries))]
-    (cond
-      (< n 1) 1
-      (> n 500) 500
-      :else n))))
+  (let [queries (-> request
+                    :params
+                    :queries)]
+    (if-let [n (if (= (re-find #"\A-?\d+" queries) nil)
+                   1
+                   (Integer/parseInt queries))]
+      (cond
+        (< n 1) 1
+        (> n 500) 500
+        :else n))))
 
 
 
 
 (defn multiple-query-test
 (defn multiple-query-test
   "Test 3: Multiple database queries"
   "Test 3: Multiple database queries"
   [request]
   [request]
   (-> request
   (-> request
-    (sanitizeQueriesParam)
-    (run-queries)
-    (bootstrap/json-response)))
+      (sanitizeQueriesParam)
+      (run-queries)
+      (bootstrap/json-response)))
 
 
 
 
 ; Set up entity Fortune and the database representation
 ; Set up entity Fortune and the database representation
@@ -92,23 +94,26 @@ Invalid (stringy) values become 1"
   (database mysql-db))
   (database mysql-db))
 
 
 
 
-(defn get-all-fortunes []
+(defn get-all-fortunes
   "Query all Fortune records from the database."
   "Query all Fortune records from the database."
+  []
   (select fortune
   (select fortune
-    (fields :id :message)))
+          (fields :id :message)))
 
 
 
 
-(defn get-fortunes []
+(defn get-fortunes
   "Fetch the full list of Fortunes from the database, sort them by the fortune
   "Fetch the full list of Fortunes from the database, sort them by the fortune
-message text, and then return the results."
+  message text, and then return the results."
+  []
   (sort-by #(:message %)
   (sort-by #(:message %)
     (conj
     (conj
       (get-all-fortunes)
       (get-all-fortunes)
       { :id 0 :message "Additional fortune added at request time." })))
       { :id 0 :message "Additional fortune added at request time." })))
 
 
 
 
-(defn fortunes-hiccup [fortunes]
+(defn fortunes-hiccup
   "Render the given fortunes to simple HTML using Hiccup."
   "Render the given fortunes to simple HTML using Hiccup."
+  [fortunes]
   (html5
   (html5
    [:head
    [:head
     [:title "Fortunes"]]
     [:title "Fortunes"]]
@@ -124,8 +129,9 @@ message text, and then return the results."
      ]]))
      ]]))
 
 
 
 
-(defn fortune-test [request]
+(defn fortune-test
   "Test 4: Fortunes"
   "Test 4: Fortunes"
+  [request]
   (->
   (->
     (get-fortunes)
     (get-fortunes)
     (fortunes-hiccup)
     (fortunes-hiccup)
@@ -136,23 +142,25 @@ message text, and then return the results."
 
 
 (defn update-and-persist
 (defn update-and-persist
   "Changes the :randomNumber of a number of world entities.
   "Changes the :randomNumber of a number of world entities.
-Persists the changes to sql then returns the updated entities"
+  Persists the changes to sql then returns the updated entities"
   [request]
   [request]
-  (let [results (-> request (sanitizeQueriesParam) (run-queries))]
+  (let [results (-> request
+                    (sanitizeQueriesParam)
+                    (run-queries))]
     (for [w results]
     (for [w results]
       (update-in w [:randomNumber (inc (rand-int 9999))]
       (update-in w [:randomNumber (inc (rand-int 9999))]
         (update world
         (update world
-          (set-fields {:randomNumber (:randomNumber w)})
-          (where {:id [:id w]}))))
-  results))
+                (set-fields {:randomNumber (:randomNumber w)})
+                (where {:id [:id w]}))))
+    results))
 
 
 
 
 (defn db-updates
 (defn db-updates
   "Test 5: Database updates"
   "Test 5: Database updates"
   [request]
   [request]
   (-> request
   (-> request
-    (update-and-persist)
-    (bootstrap/json-response)))
+      (update-and-persist)
+      (bootstrap/json-response)))
 
 
 
 
 (defn plaintext
 (defn plaintext

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

@@ -4,10 +4,10 @@ version := "1.0-SNAPSHOT"
 
 
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"
 
 
 libraryDependencies ++= Seq(
 libraryDependencies ++= Seq(
   javaJdbc,
   javaJdbc,
   javaEbean,
   javaEbean,
-  "mysql" % "mysql-connector-java" % "5.1.33"
+  "mysql" % "mysql-connector-java" % "5.1.35"
 )
 )

+ 1 - 1
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/"
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 
 // Use the Play sbt plugin for Play projects
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.7")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.9")

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

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

+ 46 - 18
frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/application.conf

@@ -29,25 +29,53 @@ application.langs="en"
 #
 #
 # You can expose this datasource via JNDI if needed (Useful for JPA)
 # You can expose this datasource via JNDI if needed (Useful for JPA)
 # db.default.jndiName=DefaultDS
 # 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
+db {
+  default {
+
+    jndiName="DefaultDS"
+
+    dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+    dataSource {
+      user=benchmarkdbuser
+      password=benchmarkdbpass
+      databaseName=hello_world
+      serverName=127.0.0.1
+      port=3306
+
+      jdbcCompliantTruncation=false
+      elideSetAutoCommits=true
+      useLocalSessionState=true
+      cachePrepStmts=true
+      cacheCallableStmts=true
+      cacheServerConfiguration=true
+      cacheResultSetMetadata=true
+      alwaysSendSetIsolation=false
+      prepStmtCacheSize=4096
+      prepStmtCacheSqlLimit=2048
+      zeroDateTimeBehavior=convertToNull
+      traceProtocol=false
+      useUnbufferedInput=false
+      useReadAheadInput=false
+      maintainTimeStats=false
+      useServerPrepStmts=true
+    }
+
+    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)
+    maxConnectionsPerPartition=64
+
+    # The number of initial connections, per partition.
+    #
+    # This maps to the minimumIdle connections for HikariCP (db.default.partitionCount * db.default.minConnectionsPerPartition)
+    minConnectionsPerPartition=64
+  }
+}
 
 
 dbplugin=disabled
 dbplugin=disabled
 
 

+ 1 - 1
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/"
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 
 // Use the Play sbt plugin for Play projects
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.7")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.9")

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

@@ -2,15 +2,15 @@ name := "play2-java-jpa-bonecp"
 
 
 version := "1.0-SNAPSHOT"
 version := "1.0-SNAPSHOT"
 
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"
 
 
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 
 
 libraryDependencies ++= Seq(
 libraryDependencies ++= Seq(
   javaJdbc,
   javaJdbc,
   javaJpa,
   javaJpa,
-  "mysql" % "mysql-connector-java" % "5.1.33",
-  "org.hibernate" % "hibernate-entitymanager" % "4.3.6.Final"
+  "mysql" % "mysql-connector-java" % "5.1.35",
+  "org.hibernate" % "hibernate-entitymanager" % "4.3.9.Final"
   )
   )
 
 
 dependencyOverrides += "com.jolbox" % "bonecp" % "0.8.0.RELEASE"
 dependencyOverrides += "com.jolbox" % "bonecp" % "0.8.0.RELEASE"

+ 1 - 1
frameworks/Java/play2-java/play2-java-jpa-bonecp/project/plugins.sbt

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

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

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

+ 47 - 19
frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/application.conf

@@ -44,26 +44,54 @@ application.langs="en"
 # You can expose this datasource via JNDI if needed (Useful for JPA)
 # You can expose this datasource via JNDI if needed (Useful for JPA)
 # db.default.jndiName=DefaultDS
 # 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
+db {
+  default {
+
+    jndiName="DefaultDS"
+
+    dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+    dataSource {
+      user=benchmarkdbuser
+      password=benchmarkdbpass
+      databaseName=hello_world
+      serverName=127.0.0.1
+      port=3306
+
+      jdbcCompliantTruncation=false
+      elideSetAutoCommits=true
+      useLocalSessionState=true
+      cachePrepStmts=true
+      cacheCallableStmts=true
+      cacheServerConfiguration=true
+      cacheResultSetMetadata=true
+      alwaysSendSetIsolation=false
+      prepStmtCacheSize=4096
+      prepStmtCacheSqlLimit=2048
+      zeroDateTimeBehavior=convertToNull
+      traceProtocol=false
+      useUnbufferedInput=false
+      useReadAheadInput=false
+      maintainTimeStats=false
+      useServerPrepStmts=true
+    }
+
+    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)
+    maxConnectionsPerPartition=64
+
+    # The number of initial connections, per partition.
+    #
+    # This maps to the minimumIdle connections for HikariCP (db.default.partitionCount * db.default.minConnectionsPerPartition)
+    minConnectionsPerPartition=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
+jpa.default=defaultPersistenceUnit
 
 
 dbplugin=disabled
 dbplugin=disabled
 
 

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

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

+ 1 - 1
frameworks/Java/play2-java/play2-java/build.sbt

@@ -4,4 +4,4 @@ version := "1.0-SNAPSHOT"
 
 
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"

+ 1 - 1
frameworks/Java/play2-java/play2-java/project/plugins.sbt

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

+ 1 - 1
frameworks/Java/play2-java/setup_java.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 
 cd play2-java
 cd play2-java
 
 

+ 1 - 1
frameworks/Java/play2-java/setup_java_ebean_bonecp.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 
 cd play2-java-ebean-bonecp
 cd play2-java-ebean-bonecp
 
 

+ 1 - 1
frameworks/Java/play2-java/setup_java_ebean_hikaricp.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 
 cd play2-java-ebean-hikaricp
 cd play2-java-ebean-hikaricp
 
 

+ 1 - 1
frameworks/Java/play2-java/setup_java_jpa_bonecp.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 
 cd play2-java-jpa-bonecp
 cd play2-java-jpa-bonecp
 
 

+ 1 - 1
frameworks/Java/play2-java/setup_java_jpa_hikaricp.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 
 cd play2-java-jpa-hikaricp
 cd play2-java-jpa-hikaricp
 
 

+ 78 - 2
frameworks/Java/sabina/benchmark_config.json

@@ -8,8 +8,59 @@
       "fortune_url": "/fortune",
       "fortune_url": "/fortune",
       "update_url": "/update?queries=",
       "update_url": "/update?queries=",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
+
       "port": 5050,
       "port": 5050,
-      "setup_file": "setup",
+      "setup_file": "undertow-mongodb",
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MongoDB",
+      "framework": "Sabina",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Sabina Undertow MongoDB",
+      "notes": "",
+      "versus": "servlet"
+    }
+  }, {
+    "undertow-mysql": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+
+      "port": 5050,
+      "setup_file": "undertow",
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MySQL",
+      "framework": "Sabina",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Sabina Undertow MySQL",
+      "notes": "",
+      "versus": "servlet"
+    }
+  }, {
+    "jetty-mysql": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+
+      "port": 5050,
+      "setup_file": "jetty",
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",
       "database": "MySQL",
       "database": "MySQL",
@@ -20,7 +71,32 @@
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "Sabina",
+      "display_name": "Sabina Jetty MySQL",
+      "notes": "",
+      "versus": "servlet"
+    }
+  }, {
+    "jetty-mongodb": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+
+      "port": 5050,
+      "setup_file": "jetty-mongodb",
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MongoDB",
+      "framework": "Sabina",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Sabina Jetty MongoDB",
       "notes": "",
       "notes": "",
       "versus": "servlet"
       "versus": "servlet"
     }
     }

+ 3 - 0
frameworks/Java/sabina/jetty-mongodb.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./setup.sh -Dsabina.backend=jetty -Dsabina.benchmark.repository=mongodb

+ 3 - 0
frameworks/Java/sabina/jetty.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./setup.sh -Dsabina.backend=jetty -Dsabina.benchmark.repository=mysql

+ 6 - 1
frameworks/Java/sabina/pom.xml

@@ -36,7 +36,7 @@
 
 
         <db.host>localhost</db.host>
         <db.host>localhost</db.host>
 
 
-        <sabina-version>1.1.1</sabina-version>
+        <sabina-version>1.3.0</sabina-version>
     </properties>
     </properties>
 
 
     <repositories>
     <repositories>
@@ -92,6 +92,11 @@
             <artifactId>mysql-connector-java</artifactId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.28</version>
             <version>5.1.28</version>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>org.mongodb</groupId>
+            <artifactId>mongo-java-driver</artifactId>
+            <version>3.0.0</version>
+        </dependency>
 
 
         <dependency>
         <dependency>
             <groupId>org.testng</groupId>
             <groupId>org.testng</groupId>

+ 59 - 134
frameworks/Java/sabina/src/main/java/sabina/benchmark/Application.java

@@ -20,39 +20,29 @@ import static sabina.Sabina.*;
 import static sabina.content.JsonContent.toJson;
 import static sabina.content.JsonContent.toJson;
 import static sabina.view.MustacheView.renderMustache;
 import static sabina.view.MustacheView.renderMustache;
 
 
-import com.mchange.v2.c3p0.ComboPooledDataSource;
 import sabina.Request;
 import sabina.Request;
+import sabina.server.MatcherFilter;
 
 
-import java.sql.*;
 import java.util.*;
 import java.util.*;
 import java.util.Date;
 import java.util.Date;
-import java.util.concurrent.ThreadLocalRandom;
-
-import javax.sql.DataSource;
+import javax.servlet.FilterConfig;
+import javax.servlet.annotation.WebFilter;
 
 
 /**
 /**
  * .
  * .
  */
  */
-final class Application {
-    private static final String SETTINGS_RESOURCE = "/server.properties";
-    private static final Properties SETTINGS = loadConfiguration ();
-
-    private static final String JDBC_URL = SETTINGS.getProperty ("mysql.uri")
-        .replace ("${db.host}", "localhost"); // TODO Move this to Gradle build
-    private static final DataSource DATA_SOURCE = createSessionFactory ();
-    private static final int DB_ROWS = 10000;
-
-    private static final boolean AUTOCOMMIT = getProperty ("sabina.benchmark.autocommit") != null;
-    private static final String SELECT_WORLD = "select * from world where id = ?";
-    private static final String UPDATE_WORLD = "update world set randomNumber = ? where id = ?";
-    private static final String SELECT_FORTUNES = "select * from fortune";
+@WebFilter ("/*")
+final class Application extends MatcherFilter {
+    static final String SETTINGS_RESOURCE = "/server.properties";
+    static final Repository REPOSITORY = loadRepository ();
+    static final int DB_ROWS = 10000;
 
 
     private static final String MESSAGE = "Hello, World!";
     private static final String MESSAGE = "Hello, World!";
     private static final String CONTENT_TYPE_TEXT = "text/plain";
     private static final String CONTENT_TYPE_TEXT = "text/plain";
     private static final String CONTENT_TYPE_JSON = "application/json";
     private static final String CONTENT_TYPE_JSON = "application/json";
     private static final String QUERIES_PARAM = "queries";
     private static final String QUERIES_PARAM = "queries";
 
 
-    private static Properties loadConfiguration () {
+    static Properties loadConfiguration () {
         try {
         try {
             Properties settings = new Properties ();
             Properties settings = new Properties ();
             settings.load (Class.class.getResourceAsStream (SETTINGS_RESOURCE));
             settings.load (Class.class.getResourceAsStream (SETTINGS_RESOURCE));
@@ -63,21 +53,43 @@ final class Application {
         }
         }
     }
     }
 
 
-    private static DataSource createSessionFactory () {
+    static Repository loadRepository () {
+        switch (getProperty ("sabina.benchmark.repository", "mysql")) {
+            case "mongodb":
+                return new MongoDbRepository (loadConfiguration ());
+            case "mysql":
+            default:
+                return new MySqlRepository (loadConfiguration ());
+        }
+    }
+
+    private static Object getDb (Request it) {
         try {
         try {
-            ComboPooledDataSource dataSource = new ComboPooledDataSource ();
-            dataSource.setMinPoolSize (32);
-            dataSource.setMaxPoolSize (256);
-            dataSource.setCheckoutTimeout (1800);
-            dataSource.setMaxStatements (50);
-            dataSource.setJdbcUrl (JDBC_URL);
-            return dataSource;
+            final World[] worlds = REPOSITORY.getWorlds (getQueries (it), false);
+            it.response.type (CONTENT_TYPE_JSON);
+            return toJson (it.queryParams (QUERIES_PARAM) == null? worlds[0] : worlds);
         }
         }
-        catch (Exception ex) {
-            throw new RuntimeException (ex);
+        catch (Exception e){
+            e.printStackTrace ();
+            throw e;
         }
         }
     }
     }
 
 
+    private static Object getFortunes (Request it) {
+        List<Fortune> fortunes = REPOSITORY.getFortunes ();
+        fortunes.add (new Fortune (0, "Additional fortune added at request time."));
+        fortunes.sort ((a, b) -> a.message.compareTo (b.message));
+
+        it.response.type ("text/html; charset=utf-8");
+        return renderMustache ("/fortunes.mustache", fortunes);
+    }
+
+    private static Object getUpdates (Request it) {
+        World[] worlds = REPOSITORY.getWorlds (getQueries (it), true);
+        it.response.type (CONTENT_TYPE_JSON);
+        return toJson (it.queryParams (QUERIES_PARAM) == null? worlds[0] : worlds);
+    }
+
     private static int getQueries (final Request request) {
     private static int getQueries (final Request request) {
         try {
         try {
             String parameter = request.queryParams (QUERIES_PARAM);
             String parameter = request.queryParams (QUERIES_PARAM);
@@ -97,118 +109,22 @@ final class Application {
         }
         }
     }
     }
 
 
-    private static Object getJson (Request it) {
-        it.response.type (CONTENT_TYPE_JSON);
-        return toJson (new Message ());
-    }
-
-    private static Object getDb (Request it) {
-        final int queries = getQueries (it);
-        final World[] worlds = new World[queries];
-
-        try (final Connection con = DATA_SOURCE.getConnection ()) {
-            final Random random = ThreadLocalRandom.current ();
-            final PreparedStatement stmt = con.prepareStatement (SELECT_WORLD);
-
-            for (int ii = 0; ii < queries; ii++) {
-                stmt.setInt (1, random.nextInt (DB_ROWS) + 1);
-                final ResultSet rs = stmt.executeQuery ();
-                while (rs.next ())
-                    worlds[ii] = new World (rs.getInt (1), rs.getInt (2));
-            }
-        }
-        catch (SQLException e) {
-            e.printStackTrace ();
-        }
-
-        it.response.type (CONTENT_TYPE_JSON);
-        return toJson (it.queryParams (QUERIES_PARAM) == null? worlds[0] : worlds);
-    }
-
-    private static Object getFortunes (Request it) {
-        final List<Fortune> fortunes = new ArrayList<> ();
-
-        try (final Connection con = DATA_SOURCE.getConnection ()) {
-            final ResultSet rs = con.prepareStatement (SELECT_FORTUNES).executeQuery ();
-            while (rs.next ())
-                fortunes.add (new Fortune (rs.getInt (1), rs.getString (2)));
-        }
-        catch (SQLException e) {
-            e.printStackTrace ();
-        }
-
-        fortunes.add (new Fortune (0, "Additional fortune added at request time."));
-        fortunes.sort ((a, b) -> a.message.compareTo (b.message));
-
-        it.response.type ("text/html; charset=utf-8");
-        return renderMustache ("/fortunes.mustache", fortunes);
-    }
-
-    private static Object getUpdates (Request it) {
-        final int queries = getQueries (it);
-        final World[] worlds = new World[queries];
-
-        try (final Connection con = DATA_SOURCE.getConnection ()) {
-            con.setAutoCommit (AUTOCOMMIT);
-
-            final Random random = ThreadLocalRandom.current ();
-            final PreparedStatement stmtSelect = con.prepareStatement (SELECT_WORLD);
-            final PreparedStatement stmtUpdate = con.prepareStatement (UPDATE_WORLD);
-
-            for (int ii = 0; ii < queries; ii++) {
-                stmtSelect.setInt (1, random.nextInt (DB_ROWS) + 1);
-                final ResultSet rs = stmtSelect.executeQuery ();
-                while (rs.next ()) {
-                    worlds[ii] = new World (rs.getInt (1), rs.getInt (2));
-                    stmtUpdate.setInt (1, random.nextInt (DB_ROWS) + 1);
-                    stmtUpdate.setInt (2, worlds[ii].id);
-
-                    if (AUTOCOMMIT) {
-                        stmtUpdate.executeUpdate ();
-                    }
-                    else {
-                        stmtUpdate.addBatch ();
-                    }
-                }
-            }
-
-            if (!AUTOCOMMIT) {
-                int count = 0;
-                boolean retrying;
-
-                do {
-                    try {
-                        stmtUpdate.executeBatch ();
-                        retrying = false;
-                    }
-                    catch (BatchUpdateException e) {
-                        retrying = true;
-                    }
-                }
-                while (count++ < 10 && retrying);
-
-                con.commit ();
-            }
-        }
-        catch (SQLException e) {
-            e.printStackTrace ();
-        }
-
-        it.response.type (CONTENT_TYPE_JSON);
-        return toJson (it.queryParams (QUERIES_PARAM) == null? worlds[0] : worlds);
-    }
-
     private static Object getPlaintext (Request it) {
     private static Object getPlaintext (Request it) {
         it.response.type (CONTENT_TYPE_TEXT);
         it.response.type (CONTENT_TYPE_TEXT);
         return MESSAGE;
         return MESSAGE;
     }
     }
 
 
+    private static Object getJson (Request it) {
+        it.response.type (CONTENT_TYPE_JSON);
+        return toJson (new Message ());
+    }
+
     private static void addCommonHeaders (Request it) {
     private static void addCommonHeaders (Request it) {
         it.header ("Server", "Undertow/1.1.2");
         it.header ("Server", "Undertow/1.1.2");
-        it.response.raw ().addDateHeader ("Date", new Date ().getTime ());
+        it.response.addDateHeader ("Date", new Date ().getTime ());
     }
     }
 
 
-    public static void main (String[] args) {
+    private static void routes () {
         get ("/json", Application::getJson);
         get ("/json", Application::getJson);
         get ("/db", Application::getDb);
         get ("/db", Application::getDb);
         get ("/query", Application::getDb);
         get ("/query", Application::getDb);
@@ -216,9 +132,18 @@ final class Application {
         get ("/update", Application::getUpdates);
         get ("/update", Application::getUpdates);
         get ("/plaintext", Application::getPlaintext);
         get ("/plaintext", Application::getPlaintext);
         after (Application::addCommonHeaders);
         after (Application::addCommonHeaders);
+    }
 
 
-        host (SETTINGS.getProperty ("web.host"));
-        port (SETTINGS.getProperty ("web.port"));
+    public static void main (String[] args) {
+        routes ();
+
+        Properties settings = loadConfiguration ();
+        host (settings.getProperty ("web.host"));
+        port (settings.getProperty ("web.port"));
         start ();
         start ();
     }
     }
+
+    @Override protected void routes (FilterConfig filterConfig) {
+        routes ();
+    }
 }
 }

+ 0 - 5
frameworks/Java/sabina/src/main/java/sabina/benchmark/Fortune.java

@@ -14,11 +14,6 @@
 
 
 package sabina.benchmark;
 package sabina.benchmark;
 
 
-/**
- * TODO .
- *
- * @author jam
- */
 final class Fortune {
 final class Fortune {
     final int id;
     final int id;
     final String message;
     final String message;

+ 91 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/MongoDbRepository.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2015 Juan José Aguililla. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+package sabina.benchmark;
+
+import static com.mongodb.client.model.Filters.eq;
+import static java.lang.Integer.parseInt;
+import static sabina.benchmark.Application.DB_ROWS;
+
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+
+import com.mongodb.*;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import org.bson.Document;
+
+final class MongoDbRepository implements Repository {
+    private MongoCollection<Document> worldCollection;
+    private MongoCollection<Document> fortuneCollection;
+
+    MongoDbRepository (Properties settings) {
+        final int PORT = parseInt (settings.getProperty ("mongodb.port"));
+        final String HOST = settings.getProperty ("mongodb.host");
+        final String DATABASE = settings.getProperty ("mongodb.database");
+        final String WORLD = settings.getProperty ("mongodb.world.collection");
+        final String FORTUNE = settings.getProperty ("mongodb.fortune.collection");
+
+        MongoClient mongoClient = new MongoClient (HOST, PORT);
+        MongoDatabase db = mongoClient.getDatabase (DATABASE);
+        worldCollection = db.getCollection (WORLD);
+        fortuneCollection = db.getCollection (FORTUNE);
+    }
+
+    @Override public List<Fortune> getFortunes () {
+        List<Fortune> fortunes = new ArrayList<> ();
+
+        fortuneCollection.find ().forEach ((Block<Document>)doc ->
+            fortunes.add (new Fortune (doc.get ("_id", Double.class).intValue (), (String)doc.get
+                ("message")))
+        );
+
+        return fortunes;
+    }
+
+    @Override public World[] getWorlds (int queries, boolean update) {
+        final World[] worlds = new World[queries];
+        final Random random = ThreadLocalRandom.current ();
+
+        for (int ii = 0; ii < queries; ii++) {
+            int id = random.nextInt (DB_ROWS) + 1;
+            worlds[ii] = update? updateWorld (id, random.nextInt (DB_ROWS) + 1) : findWorld (id);
+        }
+
+        return worlds;
+    }
+
+    private World findWorld (int id) {
+        return createWorld (worldCollection.find(eq ("_id", (double)id)).first ());
+    }
+
+    private World createWorld (Document world) {
+        try {
+            return new World (world.get ("_id", Double.class).intValue (), world.get
+                ("randomNumber", Double.class).intValue ());
+        }
+        catch (ClassCastException e) {
+            return new World (world.get ("_id", Double.class).intValue (), world.get
+                ("randomNumber", Integer.class));
+        }
+    }
+
+    public World updateWorld (int id, int random) {
+        Document newWorld = new Document ("_id", (double)id).append ("randomNumber", (double)
+            random);
+        worldCollection.replaceOne (eq ("_id", (double)id), newWorld);
+
+        return new World (id, random);
+    }
+}

+ 139 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/MySqlRepository.java

@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2015 Juan José Aguililla. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+package sabina.benchmark;
+
+import static java.lang.System.getProperty;
+import static sabina.benchmark.Application.DB_ROWS;
+
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import javax.sql.DataSource;
+
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+
+final class MySqlRepository implements Repository {
+    private static final boolean AUTOCOMMIT = getProperty ("sabina.benchmark.autocommit") != null;
+    private static final String SELECT_WORLD = "select * from world where id = ?";
+    private static final String UPDATE_WORLD = "update world set randomNumber = ? where id = ?";
+    private static final String SELECT_FORTUNES = "select * from fortune";
+
+    private final DataSource DATA_SOURCE;
+
+    MySqlRepository (Properties settings) {
+        final String jdbcUrl = settings.getProperty ("mysql.uri");
+        DATA_SOURCE = createSessionFactory (jdbcUrl);
+    }
+
+    private DataSource createSessionFactory (String jdbcUrl) {
+        try {
+            ComboPooledDataSource dataSource = new ComboPooledDataSource ();
+            dataSource.setMinPoolSize (32);
+            dataSource.setMaxPoolSize (256);
+            dataSource.setCheckoutTimeout (1800);
+            dataSource.setMaxStatements (50);
+            dataSource.setJdbcUrl (jdbcUrl);
+            return dataSource;
+        }
+        catch (Exception ex) {
+            throw new RuntimeException (ex);
+        }
+    }
+
+    private void commitUpdate (Connection con, PreparedStatement stmtUpdate)
+        throws SQLException {
+        int count = 0;
+        boolean retrying;
+
+        do {
+            try {
+                stmtUpdate.executeBatch ();
+                retrying = false;
+            }
+            catch (BatchUpdateException e) {
+                retrying = true;
+            }
+        }
+        while (count++ < 10 && retrying);
+
+        con.commit ();
+    }
+
+    private void updateWorld (World world, PreparedStatement stmtUpdate)
+        throws SQLException {
+        stmtUpdate.setInt (1, world.randomNumber);
+        stmtUpdate.setInt (2, world.id);
+
+        if (AUTOCOMMIT) {
+            stmtUpdate.executeUpdate ();
+        }
+        else {
+            stmtUpdate.addBatch ();
+        }
+    }
+
+    @Override public List<Fortune> getFortunes () {
+        final List<Fortune> fortunes = new ArrayList<> ();
+
+        try (final Connection con = DATA_SOURCE.getConnection ()) {
+            final ResultSet rs = con.prepareStatement (SELECT_FORTUNES).executeQuery ();
+            while (rs.next ())
+                fortunes.add (new Fortune (rs.getInt (1), rs.getString (2)));
+        }
+        catch (SQLException e) {
+            e.printStackTrace ();
+        }
+
+        return fortunes;
+    }
+
+    @Override public World[] getWorlds (int queries, boolean update) {
+        final World[] worlds = new World[queries];
+
+        try (final Connection con = DATA_SOURCE.getConnection ()) {
+            if (update)
+                con.setAutoCommit (AUTOCOMMIT);
+
+            final Random random = ThreadLocalRandom.current ();
+            final PreparedStatement stmtSelect = con.prepareStatement (SELECT_WORLD);
+            final PreparedStatement stmtUpdate = update? con.prepareStatement (UPDATE_WORLD) : null;
+
+            for (int ii = 0; ii < queries; ii++) {
+                stmtSelect.setInt (1, random.nextInt (DB_ROWS) + 1);
+                final ResultSet rs = stmtSelect.executeQuery ();
+                while (rs.next ()) {
+                    worlds[ii] = new World (rs.getInt (1), rs.getInt (2));
+
+                    if (update) {
+                        worlds[ii] = new World (worlds[ii].id, random.nextInt (DB_ROWS) + 1);
+                        updateWorld (worlds[ii], stmtUpdate);
+                    }
+                }
+            }
+
+            if (update && !AUTOCOMMIT)
+                commitUpdate (con, stmtUpdate);
+        }
+        catch (SQLException e) {
+            e.printStackTrace ();
+        }
+
+        return worlds;
+    }
+}

+ 22 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/Repository.java

@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2015 Juan José Aguililla. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+package sabina.benchmark;
+
+import java.util.List;
+
+interface Repository {
+    List<Fortune> getFortunes ();
+    World[] getWorlds (int queries, boolean update);
+}

+ 5 - 2
frameworks/Java/sabina/src/main/resources/server.properties

@@ -15,8 +15,11 @@
 web.port = 5050
 web.port = 5050
 web.host = 0.0.0.0
 web.host = 0.0.0.0
 
 
-mongodb.uri = ${db.host}:27017
-mongodb.name = hello_world
+mongodb.host = ${db.host}
+mongodb.port = 27017
+mongodb.database = hello_world
+mongodb.world.collection = world
+mongodb.fortune.collection = fortune
 
 
 mysql.uri = jdbc:mysql://${db.host}:3306/hello_world?\
 mysql.uri = jdbc:mysql://${db.host}:3306/hello_world?\
 user=benchmarkdbuser&\
 user=benchmarkdbuser&\

+ 13 - 28
frameworks/Java/sabina/src/test/java/sabina/benchmark/ApplicationTest.java

@@ -16,7 +16,6 @@ package sabina.benchmark;
 
 
 import static org.apache.http.client.fluent.Request.Get;
 import static org.apache.http.client.fluent.Request.Get;
 import static org.testng.AssertJUnit.*;
 import static org.testng.AssertJUnit.*;
-import static sabina.benchmark.Application.main;
 import static sabina.Sabina.stop;
 import static sabina.Sabina.stop;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -39,37 +38,13 @@ import org.testng.annotations.Test;
  * <p>TODO Change assert's order
  * <p>TODO Change assert's order
  */
  */
 public final class ApplicationTest {
 public final class ApplicationTest {
-    private static final int THREADS = 16, EXECUTIONS = 32, WARM_UP = 32;
+    private static final int THREADS = 1, EXECUTIONS = 1;
 
 
     private static final String ENDPOINT = "http://localhost:5050";
     private static final String ENDPOINT = "http://localhost:5050";
     private static final Gson GSON = new Gson ();
     private static final Gson GSON = new Gson ();
 
 
     @BeforeClass public static void setup () {
     @BeforeClass public static void setup () {
-        main (null);
-    }
-
-    @BeforeClass public void warm_up () throws IOException {
-        for (int ii = 0; ii < WARM_UP; ii++) {
-            json ();
-            plaintext ();
-            no_query_parameter ();
-            empty_query_parameter ();
-            text_query_parameter ();
-            zero_queries ();
-            one_thousand_queries ();
-            one_query ();
-            ten_queries ();
-            five_hundred_queries ();
-            fortunes ();
-            no_updates_parameter ();
-            empty_updates_parameter ();
-            text_updates_parameter ();
-            zero_updates ();
-            one_thousand_updates ();
-            one_update ();
-            ten_updates ();
-            five_hundred_updates ();
-        }
+        Application.main (null);
     }
     }
 
 
     @AfterClass public static void close () {
     @AfterClass public static void close () {
@@ -134,6 +109,11 @@ public final class ApplicationTest {
         checkDbRequest ("/query?queries=10", 10);
         checkDbRequest ("/query?queries=10", 10);
     }
     }
 
 
+    @Test(threadPoolSize = THREADS, invocationCount = EXECUTIONS)
+    public void one_hundred_queries () throws IOException {
+        checkDbRequest ("/query?queries=100", 100);
+    }
+
     @Test(threadPoolSize = THREADS, invocationCount = EXECUTIONS)
     @Test(threadPoolSize = THREADS, invocationCount = EXECUTIONS)
     public void five_hundred_queries () throws IOException {
     public void five_hundred_queries () throws IOException {
         checkDbRequest ("/query?queries=500", 500);
         checkDbRequest ("/query?queries=500", 500);
@@ -192,6 +172,11 @@ public final class ApplicationTest {
         checkDbRequest ("/update?queries=10", 10);
         checkDbRequest ("/update?queries=10", 10);
     }
     }
 
 
+    @Test(threadPoolSize = THREADS, invocationCount = EXECUTIONS)
+    public void one_hundred_updates () throws IOException {
+        checkDbRequest ("/update?queries=100", 100);
+    }
+
     @Test(threadPoolSize = THREADS, invocationCount = EXECUTIONS)
     @Test(threadPoolSize = THREADS, invocationCount = EXECUTIONS)
     public void five_hundred_updates () throws IOException {
     public void five_hundred_updates () throws IOException {
         checkDbRequest ("/update?queries=500", 500);
         checkDbRequest ("/update?queries=500", 500);
@@ -218,7 +203,7 @@ public final class ApplicationTest {
         assertTrue (res.getFirstHeader ("Server") != null);
         assertTrue (res.getFirstHeader ("Server") != null);
         assertTrue (res.getFirstHeader ("Date") != null);
         assertTrue (res.getFirstHeader ("Date") != null);
         assertEquals (content.length (), res.getEntity ().getContentLength ());
         assertEquals (content.length (), res.getEntity ().getContentLength ());
-        assertEquals (contentType, res.getEntity ().getContentType ().getValue ());
+        assertTrue (res.getEntity ().getContentType ().getValue ().contains (contentType));
     }
     }
 
 
     private void checkResultItems (String result, int size) {
     private void checkResultItems (String result, int size) {

+ 3 - 0
frameworks/Java/sabina/undertow-mongodb.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./setup.sh -Dsabina.backend=undertow -Dsabina.benchmark.repository=mongodb

+ 3 - 0
frameworks/Java/sabina/undertow.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./setup.sh -Dsabina.backend=undertow -Dsabina.benchmark.repository=mysql

+ 45 - 8
frameworks/Java/vertx/README.md

@@ -1,26 +1,63 @@
-# Vertx Benchmarking Test
+# Vertx 2.x Benchmarking Test
 
 
-This is the vertx portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+This is the vertx 2.x portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### Plaintext Test
+
+* [Plaintext test source](WebServer.java)
+
+### JSON Serialization Test
 
 
-### JSON Encoding Test
 * [JSON test source](WebServer.java)
 * [JSON test source](WebServer.java)
 
 
-### Data-Store/Database Mapping Test
+### Database Single query Test
+
+* [Database Single query test source](WebServer.java)
+
+### Database Multiple queries Test
+
+* [Database Multiple queries test source](WebServer.java)
+
+### Database Data updates Test
+
+* [Database Data updates test source](WebServer.java)
+
+### Fortunes Test
 
 
-* [Database test source](WebServer.java)
+* [Fortunes test source](WebServer.java)
 
 
 ## Versions
 ## Versions
 
 
-* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
-* [vertx 1.3.1](http://vertx.io/)
+* [Java OpenJDK 1.7.0_79](http://openjdk.java.net/)
+* [vertx 2.1.5](http://vertx.io/)
 
 
 
 
 ## Test URLs
 ## Test URLs
 
 
+### Plaintext Test
+
+    http://localhost:8080/plaintext
+
 ### JSON Encoding Test
 ### JSON Encoding Test
 
 
     http://localhost:8080/json
     http://localhost:8080/json
 
 
 ### Database Mapping Test
 ### Database Mapping Test
 
 
-    http://localhost:8080/db?queries=5
+    http://localhost:8080/db?queries=5
+
+### Database Single query Test
+
+    http://localhost:8080/db
+
+### Database Multiple queries Test
+
+    http://localhost:8080/queries?queries=5
+
+### Database Data updates Test
+
+    http://localhost:8080/updates?queries=3
+
+### Fortunes Test
+
+    http://localhost:8080/fortunes

+ 165 - 41
frameworks/Java/vertx/WebServer.java

@@ -15,15 +15,67 @@ import java.util.Date;
 import java.util.Random;
 import java.util.Random;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
+import freemarker.template.Template;
+import freemarker.template.Configuration;
+import java.io.StringReader;
+import java.io.Writer;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+
 public class WebServer extends Verticle implements Handler<HttpServerRequest> {
 public class WebServer extends Verticle implements Handler<HttpServerRequest> {
 
 
-  private Buffer helloWorldBuffer = new Buffer("Hello, World!");
-  private String helloWorldContentLength = String.valueOf(helloWorldBuffer.length());
-  private DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss z");
+  private final Buffer helloWorldBuffer = new Buffer("Hello, World!");
+  private final String helloWorldContentLength = String.valueOf(helloWorldBuffer.length());
+  private final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss z");
+  private final Random random = ThreadLocalRandom.current();
   private String dateString;
   private String dateString;
 
 
+  private static final String PATH_PLAINTEXT = "/plaintext";
+  private static final String PATH_JSON = "/json";
+  private static final String PATH_DB = "/db";
+  private static final String PATH_QUERIES = "/queries";
+  private static final String PATH_UPDATES = "/updates";
+  private static final String PATH_FORTUNES = "/fortunes";
+  private static final String RESPONSE_TYPE_PLAIN = "text/plain";
+  private static final String RESPONSE_TYPE_HTML = "text/html";
+  private static final String RESPONSE_TYPE_JSON = "application/json";
+  private static final String HEADER_CONTENT_TYPE = "Content-Type";
+  private static final String HEADER_CONTENT_LENGTH = "Content-Length";
+  private static final String HEADER_SERVER = "Server";
+  private static final String HEADER_SERVER_VERTX = "vert.x";
+  private static final String HEADER_DATE = "Date";
+  private static final String MONGO_ADDRESS = "hello.persistor";
+  private static final String FREEMARKER_ADDRESS = "vertx.freemarker";
+  private static final String UNDERSCORE_ID = "_id";
+  private static final String TEXT_ID = "id";
+  private static final String RANDOM_NUMBER = "randomNumber";
+  private static final String TEXT_RESULT = "result";
+  private static final String TEXT_RESULTS = "results";
+  private static final String TEXT_QUERIES = "queries";
+  private static final String TEXT_MESSAGE = "message";
+  private static final String TEXT_MESSAGES = "messages";
+  private static final String ADD_FORTUNE_MESSAGE = "Additional fortune added at request time.";
+  private static final String HELLO_WORLD = "Hello, world!";
+  private static final String TEXT_ACTION = "action";
+  private static final String TEXT_CRITERIA = "criteria";
+  private static final String TEXT_UPDATE = "update";
+  private static final String TEXT_OBJ_NEW = "objNew";
+  private static final String TEXT_FINDONE = "findone";
+  private static final String TEXT_FIND = "find";
+  private static final String TEXT_COLLECTION = "collection";
+  private static final String TEXT_WORLD = "World";
+  private static final String TEXT_FORTUNE = "Fortune";
+  private static final String TEXT_MATCHER = "matcher";
+  private static final String TEMPLATE_FORTUNE = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr><#list messages as message><tr><td>${message.id?html}</td><td>${message.message?html}</td></tr></#list></table></body></html>";
+  
+  private Template ftlTemplate;
+
   @Override
   @Override
   public void start() {
   public void start() {
+    try { ftlTemplate = new Template(TEXT_FORTUNE, new StringReader(TEMPLATE_FORTUNE), new Configuration(Configuration.VERSION_2_3_22)); } catch (Exception ex) { ex.printStackTrace(); }
     vertx.createHttpServer().requestHandler(WebServer.this).listen(8080);
     vertx.createHttpServer().requestHandler(WebServer.this).listen(8080);
     vertx.setPeriodic(1000, new Handler<Long>() {
     vertx.setPeriodic(1000, new Handler<Long>() {
       @Override
       @Override
@@ -36,19 +88,24 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
 
 
   @Override
   @Override
   public void handle(HttpServerRequest req) {
   public void handle(HttpServerRequest req) {
-    String path = req.path();
-    switch (path) {
-      case "/plaintext":
+    switch (req.path()) {
+      case PATH_PLAINTEXT:
         handlePlainText(req);
         handlePlainText(req);
         break;
         break;
-      case "/json":
+      case PATH_JSON:
         handleJson(req);
         handleJson(req);
         break;
         break;
-      case "/db":
+      case PATH_DB:
         handleDbMongo(req);
         handleDbMongo(req);
         break;
         break;
-      case "/queries":
-        handleQueriesMongo(req);
+      case PATH_QUERIES:
+        handleDBMongo(req,false);
+        break;
+      case PATH_UPDATES:
+        handleDBMongo(req,true);
+        break;
+      case PATH_FORTUNES:
+        handleFortunes(req);
         break;
         break;
       default:
       default:
         req.response().setStatusCode(404);
         req.response().setStatusCode(404);
@@ -60,21 +117,55 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
     dateString = DATE_FORMAT.format(new Date());
     dateString = DATE_FORMAT.format(new Date());
   }
   }
 
 
+  private void handleFortunes(HttpServerRequest req) {
+    final HttpServerResponse resp = req.response();
+    
+    vertx.eventBus().send(
+      MONGO_ADDRESS,
+      new JsonObject()
+          .putString(TEXT_ACTION, TEXT_FIND)
+          .putString(TEXT_COLLECTION, TEXT_FORTUNE),
+      new Handler<Message<JsonObject>>() {
+        @Override
+        public void handle(Message<JsonObject> reply) {
+          JsonArray results = reply.body().getArray(TEXT_RESULTS);
+          
+          List<Fortune> fortunes = new ArrayList<>();
+          for (Object fortune: results) {
+            fortunes.add(new Fortune(
+              ((JsonObject)fortune).getNumber(TEXT_ID).intValue(),
+              ((JsonObject)fortune).getString(TEXT_MESSAGE)));
+          }            
+          fortunes.add(new Fortune(0, ADD_FORTUNE_MESSAGE));
+          Collections.sort(fortunes);
+
+          Map model = new HashMap();
+          model.put(TEXT_MESSAGES, fortunes);
+          Writer writer = new StringWriter();
+          try { ftlTemplate.process(model, writer); } catch (Exception ex) { ex.printStackTrace(); }
+
+          Buffer buff = new Buffer(writer.toString());
+          setHeaders(resp, RESPONSE_TYPE_HTML, String.valueOf(buff.length()));
+          resp.end(buff);
+        }  
+    });
+  }
+
   private void handlePlainText(HttpServerRequest req) {
   private void handlePlainText(HttpServerRequest req) {
     HttpServerResponse resp = req.response();
     HttpServerResponse resp = req.response();
-    setHeaders(resp, "text/plain", helloWorldContentLength);
+    setHeaders(resp, RESPONSE_TYPE_PLAIN, helloWorldContentLength);
     resp.end(helloWorldBuffer);
     resp.end(helloWorldBuffer);
   }
   }
 
 
   private void handleJson(HttpServerRequest req) {
   private void handleJson(HttpServerRequest req) {
-    Buffer buff = new Buffer(Json.encode(Collections.singletonMap("message", "Hello, world!")));
+    Buffer buff = new Buffer(Json.encode(Collections.singletonMap(TEXT_MESSAGE, HELLO_WORLD)));
     HttpServerResponse resp = req.response();
     HttpServerResponse resp = req.response();
-    setHeaders(resp, "application/json", String.valueOf(buff.length()));
+    setHeaders(resp, RESPONSE_TYPE_JSON, String.valueOf(buff.length()));
     resp.end(buff);
     resp.end(buff);
   }
   }
 
 
   private void handleDbMongo(final HttpServerRequest req) {
   private void handleDbMongo(final HttpServerRequest req) {
-    findRandom(ThreadLocalRandom.current(), new Handler<Message<JsonObject>>() {
+    findRandom(new Handler<Message<JsonObject>>() {
       @Override
       @Override
       public void handle(Message<JsonObject> reply) {
       public void handle(Message<JsonObject> reply) {
         JsonObject world = getResultFromReply(reply);
         JsonObject world = getResultFromReply(reply);
@@ -86,72 +177,86 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
 
 
   private JsonObject getResultFromReply(Message<JsonObject> reply) {
   private JsonObject getResultFromReply(Message<JsonObject> reply) {
     JsonObject body = reply.body();
     JsonObject body = reply.body();
-    JsonObject world = body.getObject("result");
-    Object id = world.removeField("_id");
-    world.putValue("id", id);
+    JsonObject world = body.getObject(TEXT_RESULT);
+    Object id = world.removeField(UNDERSCORE_ID);
+    world.putValue(TEXT_ID, Integer.valueOf(((Double)id).intValue()));
     return world;
     return world;
   }
   }
 
 
-  private void handleQueriesMongo(final HttpServerRequest req) {
+  private void handleDBMongo(final HttpServerRequest req, boolean randomUpdates) {
     int queriesParam = 1;
     int queriesParam = 1;
     try {
     try {
-      queriesParam = Integer.parseInt(req.params().get("queries"));
+      queriesParam = Integer.parseInt(req.params().get(TEXT_QUERIES));
     } catch (NumberFormatException e) {
     } catch (NumberFormatException e) {
-      e.printStackTrace();
-    }
-    if (queriesParam < 1)
-    {
       queriesParam = 1;
       queriesParam = 1;
     }
     }
-    if (queriesParam > 500)
-    {
+    if (queriesParam < 1) {
+      queriesParam = 1;
+    } else if (queriesParam > 500) {
       queriesParam = 500;
       queriesParam = 500;
     }
     }
-    final MongoHandler dbh = new MongoHandler(req, queriesParam);
-    final Random random = ThreadLocalRandom.current();
+    final MongoHandler dbh = new MongoHandler(req, queriesParam, randomUpdates);
     for (int i = 0; i < queriesParam; i++) {
     for (int i = 0; i < queriesParam; i++) {
-      findRandom(random, dbh);
+      findRandom(dbh);
     }
     }
   }
   }
 
 
-  private void findRandom(Random random, Handler<Message<JsonObject>> handler) {
+  private void findRandom(Handler<Message<JsonObject>> handler) {
     vertx.eventBus().send(
     vertx.eventBus().send(
-        "hello.persistor",
+        MONGO_ADDRESS,
         new JsonObject()
         new JsonObject()
-            .putString("action", "findone")
-            .putString("collection", "World")
-            .putObject("matcher", new JsonObject().putNumber("_id", (random.nextInt(10000) + 1))),
+            .putString(TEXT_ACTION, TEXT_FINDONE)
+            .putString(TEXT_COLLECTION, TEXT_WORLD)
+            .putObject(TEXT_MATCHER, new JsonObject().putNumber(UNDERSCORE_ID, (random.nextInt(10000) + 1))),
         handler);
         handler);
   }
   }
 
 
+  private void updateRandom(JsonObject json) {
+    vertx.eventBus().send(
+        MONGO_ADDRESS,
+        new JsonObject()
+            .putString(TEXT_ACTION, TEXT_UPDATE)
+            .putString(TEXT_COLLECTION, TEXT_WORLD)
+            .putObject(TEXT_CRITERIA, new JsonObject().putValue(UNDERSCORE_ID, json.getValue(TEXT_ID)))
+            .putObject(TEXT_OBJ_NEW, json)
+             );
+  }
+
   private void sendResponse(HttpServerRequest req, String result) {
   private void sendResponse(HttpServerRequest req, String result) {
     Buffer buff = new Buffer(result);
     Buffer buff = new Buffer(result);
     HttpServerResponse resp = req.response();
     HttpServerResponse resp = req.response();
-    setHeaders(resp, "application/json", String.valueOf(buff.length()));
+    setHeaders(resp, RESPONSE_TYPE_JSON, String.valueOf(buff.length()));
     resp.end(buff);
     resp.end(buff);
   }
   }
 
 
   private void setHeaders(HttpServerResponse resp, String contentType, String contentLength) {
   private void setHeaders(HttpServerResponse resp, String contentType, String contentLength) {
-    resp.putHeader("Content-Type", contentType);
-    resp.putHeader("Content-Length", contentLength);
-    resp.putHeader("Server", "vert.x");
-    resp.putHeader("Date", dateString);
+    resp.putHeader(HEADER_CONTENT_TYPE, contentType);
+    resp.putHeader(HEADER_CONTENT_LENGTH, contentLength);
+    resp.putHeader(HEADER_SERVER, HEADER_SERVER_VERTX );
+    resp.putHeader(HEADER_DATE, dateString);
   }
   }
 
 
-  private class MongoHandler implements Handler<Message<JsonObject>> {
+  private final class MongoHandler implements Handler<Message<JsonObject>> {
     private final HttpServerRequest req;
     private final HttpServerRequest req;
     private final int queries;
     private final int queries;
     private final JsonArray worlds;
     private final JsonArray worlds;
+    private final Random random;
+    private final boolean randomUpdates;
 
 
-    public MongoHandler(HttpServerRequest request, int queriesParam) {
+    public MongoHandler(HttpServerRequest request, int queriesParam, boolean performRandomUpdates) {
       req = request;
       req = request;
       queries = queriesParam;
       queries = queriesParam;
+      randomUpdates = performRandomUpdates;
+      random = ThreadLocalRandom.current();
       worlds = new JsonArray();
       worlds = new JsonArray();
     }
     }
-
     @Override
     @Override
     public void handle(Message<JsonObject> reply) {
     public void handle(Message<JsonObject> reply) {
       JsonObject world = getResultFromReply(reply);
       JsonObject world = getResultFromReply(reply);
+      if (randomUpdates) {
+        world.putValue(RANDOM_NUMBER, (random.nextInt(10000) + 1));
+        updateRandom(world);        
+      }
       worlds.add(world);
       worlds.add(world);
       if (worlds.size() == this.queries) {
       if (worlds.size() == this.queries) {
         // All queries have completed; send the response.
         // All queries have completed; send the response.
@@ -160,6 +265,25 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
       }
       }
     }
     }
   }
   }
+  
+  public final class Fortune implements Comparable<Fortune> {
+    public int id;
+    public String message;
 
 
+    public int getId() {
+      return id;
+    }
+    public String getMessage() {
+      return message;
+    }
+    public Fortune(int id, String message) {
+      this.id = id;
+      this.message = message;
+    }
+    @Override
+    public int compareTo(Fortune other) {
+      return message.compareTo(other.message);
+    }
+  }  
 }
 }
 
 

+ 2 - 1
frameworks/Java/vertx/app.js

@@ -3,7 +3,8 @@ var container = require('vertx/container')
 var persistorConf = {
 var persistorConf = {
   address: 'hello.persistor',
   address: 'hello.persistor',
   db_name: 'hello_world',
   db_name: 'hello_world',
-  host: 'localhost'
+  host: '127.0.0.1',
+  pool_size: 100
 }
 }
 
 
 container.deployModule('io.vertx~mod-mongo-persistor~2.1.1', persistorConf, function (err, dep_id) {
 container.deployModule('io.vertx~mod-mongo-persistor~2.1.1', persistorConf, function (err, dep_id) {

+ 2 - 0
frameworks/Java/vertx/benchmark_config.json

@@ -7,6 +7,8 @@
       "db_url": "/db",
       "db_url": "/db",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "query_url": "/queries?queries=",
       "query_url": "/queries?queries=",
+      "update_url": "/updates?queries=",
+      "fortune_url": "/fortunes",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",

+ 1 - 1
frameworks/Java/vertx/source_code

@@ -1,2 +1,2 @@
-./vertx/App.groovy
+./vertx/app.js
 ./vertx/WebServer.java
 ./vertx/WebServer.java

+ 2 - 2
frameworks/JavaScript/express/README.md

@@ -12,8 +12,8 @@ This is the Express portion of a [benchmarking test suite](../) comparing a vari
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 The tests were run with:
 The tests were run with:
-* [Node.js v0.10.0](http://nodejs.org/)
-* [Express 3.1](http://expressjs.com/)
+* [Node.js v0.12.2](http://nodejs.org/)
+* [Express 4.12.3](http://expressjs.com/)
 
 
 ## Resources
 ## Resources
 * http://nodejs.org/api/cluster.html
 * http://nodejs.org/api/cluster.html

+ 104 - 64
frameworks/JavaScript/express/app.js

@@ -5,28 +5,56 @@
 
 
 var cluster = require('cluster')
 var cluster = require('cluster')
   , numCPUs = require('os').cpus().length
   , numCPUs = require('os').cpus().length
-  , windows = require('os').platform() == 'win32'
   , express = require('express')
   , express = require('express')
+  , Sequelize = require('sequelize')
   , mongoose = require('mongoose')
   , mongoose = require('mongoose')
-  , async = require('async')
   , conn = mongoose.connect('mongodb://localhost/hello_world')
   , conn = mongoose.connect('mongodb://localhost/hello_world')
-  , connMap = { user: 'benchmarkdbuser', password: 'benchmarkdbpass', database: 'hello_world', host: 'localhost', charset: 'utf8' };
+  , async = require('async');
+
+// Middleware
+var bodyParser = require('body-parser')
+  , methodOverride = require('method-override')
+  , errorHandler = require('errorhandler');
 
 
 var Schema = mongoose.Schema
 var Schema = mongoose.Schema
   , ObjectId = Schema.ObjectId;
   , ObjectId = Schema.ObjectId;
 
 
-var WorldSchema = new Schema({
-    id                           : Number
-  , randomNumber                 : Number
-}, { collection : 'world' });
-var MWorld = conn.model('World', WorldSchema);
-
-if (!windows) {
-  var Mapper = require('mapper');
-  Mapper.connect(connMap, {verbose: false, strict: false});
-  var World = Mapper.map("World", "id", "randomNumber");
-  var Fortune = Mapper.map("Fortune", "id", "message");
-}
+var WorldSchema = new mongoose.Schema({
+    id          : Number,
+    randomNumber: Number
+  }, {
+    collection: 'world'
+  }),
+  MWorld = conn.model('World', WorldSchema);
+
+var sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'localhost',
+  dialect: 'mysql',
+  logging: false
+});
+
+var World = sequelize.define('World', {
+  id: {
+    type: 'Sequelize.INTEGER'
+  },
+  randomNumber: {
+    type: 'Sequelize.INTEGER'
+  }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+var Fortune = sequelize.define('Fortune', {
+  id: {
+    type: 'Sequelize.INTEGER'
+  },
+  message: {
+    type: 'Sequelize.STRING'
+  }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
 
 
 if (cluster.isMaster) {
 if (cluster.isMaster) {
   // Fork workers.
   // Fork workers.
@@ -41,95 +69,104 @@ if (cluster.isMaster) {
   var app = module.exports = express();
   var app = module.exports = express();
 
 
   // Configuration
   // Configuration
-  app.configure(function(){
-    app.use(express.bodyParser());
-    app.use(express.methodOverride());
-    app.use(app.router);
+  // https://github.com/expressjs/method-override#custom-logic
+  app.use(bodyParser.urlencoded({extended: true}));
+  app.use(methodOverride(function(req, res){
+    if (req.body && typeof req.body === 'object' && '_method' in req.body) {
+      // look in urlencoded POST bodies and delete it
+      var method = req.body._method
+      delete req.body._method
+      return method
+    }
+  }));
 
 
-    app.set('view engine', 'jade');
-    app.set('views', __dirname + '/views');
+  // Set headers for all routes
+  app.use(function(req, res, next) {
+    res.setHeader("Server", "Express");
+    return next();
   });
   });
 
 
-  app.configure('development', function() {
-    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
-  });
+  app.set('view engine', 'jade');
+  app.set('views', __dirname + '/views');
 
 
-  app.configure('production', function() {
-    app.use(express.errorHandler());
-  });
+  // Check Node env.
+  var env = process.env.NODE_ENV || 'development';
+  if ('development' == env) {
+    app.use(errorHandler({ dumpExceptions: true, showStack: true }));
+  }
+  if ('production' == env) {
+    app.use(errorHandler());
+  }
 
 
   // Routes
   // Routes
 
 
   app.get('/json', function(req, res) {
   app.get('/json', function(req, res) {
-    res.send({ message: 'Hello, World!' })
+    res.send({ message: 'Hello, World!' });
+  });
+
+  app.get('/plaintext', function(req, res) {
+    res.header('Content-Type', 'text/plain').send('Hello, World!');
   });
   });
   
   
   app.get('/mongoose', function(req, res) {
   app.get('/mongoose', function(req, res) {
-    var queries = req.query.queries || 1,
-        worlds  = [],
-        queryFunctions = [];
+    var queries = isNaN(req.query.queries) ? 1 : parseInt(req.query.queries, 10)
+      , queryFunctions = [];
+
+    queries = Math.min(Math.max(queries, 1), 500);
 
 
     for (var i = 1; i <= queries; i++ ) {
     for (var i = 1; i <= queries; i++ ) {
       queryFunctions.push(function(callback) {
       queryFunctions.push(function(callback) {
-        MWorld.findOne({ id: (Math.floor(Math.random() * 10000) + 1 )}).exec(function (err, world) {
-          worlds.push(world);
-          callback(null, 'success');
-        });
+        MWorld.findOne({ id: (Math.floor(Math.random() * 10000) + 1) }).exec(callback);
       });
       });
     }
     }
 
 
     async.parallel(queryFunctions, function(err, results) {
     async.parallel(queryFunctions, function(err, results) {
-      if (queries == 1) {
-        worlds = worlds[0];
+      if (!req.query.queries) {
+        results = results[0];
       }
       }
-      res.send(worlds);
+      res.send(results);
     });
     });
   });
   });
 
 
-  app.get('/mysql-orm', function(req, res) {
-    if (windows) return res.send(501, 'Not supported on windows');
-    
+  app.get('/mysql-orm', function(req, res) {    
     var queries = isNaN(req.query.queries) ? 1 : parseInt(req.query.queries, 10)
     var queries = isNaN(req.query.queries) ? 1 : parseInt(req.query.queries, 10)
-      , worlds  = []
       , queryFunctions = [];
       , queryFunctions = [];
 
 
     queries = Math.min(Math.max(queries, 1), 500);
     queries = Math.min(Math.max(queries, 1), 500);
 
 
     for (var i = 1; i <= queries; i++ ) {
     for (var i = 1; i <= queries; i++ ) {
       queryFunctions.push(function(callback) {
       queryFunctions.push(function(callback) {
-        World.findById(Math.floor(Math.random()*10000) + 1, function (err, world) {
-          worlds.push(world);
-          callback(null, 'success');
-        });
+        World.findOne({
+          where: {
+            id: Math.floor(Math.random() * 10000) + 1}
+          }
+        ).complete(callback);
       });
       });
     }
     }
 
 
     async.parallel(queryFunctions, function(err, results) {
     async.parallel(queryFunctions, function(err, results) {
       if (!req.query.queries) {
       if (!req.query.queries) {
-        worlds = worlds[0];
+        results = results[0];
       }
       }
-      res.send(worlds);
+      res.setHeader("Content-Type", "application/json");
+      res.send(results);
     });
     });
   });
   });
 
 
   app.get('/fortune', function(req, res) {
   app.get('/fortune', function(req, res) {
-    if (windows) return res.send(501, 'Not supported on windows');
-    
-    Fortune.all(function (err, fortunes) {
+    Fortune.findAll().complete(function (err, fortunes) {
       var newFortune = {id: 0, message: "Additional fortune added at request time."};
       var newFortune = {id: 0, message: "Additional fortune added at request time."};
       fortunes.push(newFortune);
       fortunes.push(newFortune);
-      fortunes.sort(sortFortunes);
+      fortunes.sort(function (a, b) {
+        return (a.message < b.message) ? -1 : 1;
+      });
 
 
       res.render('fortunes', {fortunes: fortunes});
       res.render('fortunes', {fortunes: fortunes});
     });
     });
   });
   });
 
 
-  function sortFortunes(a, b) {
-    return (a.message < b.message) ? -1 : (a.message > b.message) ? 1 : 0;
-  }
-
   app.get('/mongoose-update', function(req, res) {
   app.get('/mongoose-update', function(req, res) {
-    var queries = req.query.queries || 1
+    var queries = isNaN(req.query.queries) ? 1 : parseInt(req.query.queries, 10)
       , selectFunctions = [];
       , selectFunctions = [];
 
 
     queries = Math.min(queries, 500);
     queries = Math.min(queries, 500);
@@ -163,16 +200,18 @@ if (cluster.isMaster) {
   });
   });
 
 
   app.get('/mysql-orm-update', function(req, res) {
   app.get('/mysql-orm-update', function(req, res) {
-    if (windows) return res.send(501, 'Not supported on windows');
-
-    var queries = req.query.queries || 1
+    var queries = isNaN(req.query.queries) ? 1 : parseInt(req.query.queries, 10)
       , selectFunctions = [];
       , selectFunctions = [];
 
 
-    queries = Math.min(queries, 500);
+    queries = Math.max(Math.min(queries, 500), 1);
 
 
     for (var i = 1; i <= queries; i++ ) {
     for (var i = 1; i <= queries; i++ ) {
       selectFunctions.push(function(callback) {
       selectFunctions.push(function(callback) {
-        World.findById(Math.floor(Math.random() * 10000) + 1, callback);
+        World.findOne({
+          where: {
+            id: Math.floor(Math.random() * 10000) + 1}
+          }
+        ).complete(callback);
       });
       });
     }
     }
 
 
@@ -183,7 +222,7 @@ if (cluster.isMaster) {
         (function(i){
         (function(i){
           updateFunctions.push(function(callback){
           updateFunctions.push(function(callback){
             worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
             worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
-            World.save(worlds[i], callback);
+            worlds[i].save().complete(callback);
           });
           });
         })(i);
         })(i);
       }
       }
@@ -191,7 +230,8 @@ if (cluster.isMaster) {
       async.parallel(updateFunctions, function(err, updates) {
       async.parallel(updateFunctions, function(err, updates) {
         res.send(worlds);
         res.send(worlds);
       });
       });
-    });   
+    });  
+
   });
   });
 
 
   app.listen(8080);
   app.listen(8080);

+ 1 - 0
frameworks/JavaScript/express/benchmark_config.json

@@ -4,6 +4,7 @@
     "default": {
     "default": {
       "setup_file": "setup",
       "setup_file": "setup",
       "json_url": "/json",
       "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",

+ 3 - 0
frameworks/JavaScript/express/install.sh

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

+ 8 - 5
frameworks/JavaScript/express/package.json

@@ -3,11 +3,14 @@
   , "version": "0.0.1"
   , "version": "0.0.1"
   , "private": true
   , "private": true
   , "dependencies": {
   , "dependencies": {
-      "express": "3.1.0"
-    , "mongoose": "3.5.5" 
-    , "async": "0.2.5"
-    , "mysql-libmysqlclient": "1.5.2"
-    , "mapper": "0.2.4-pre"
+      "express": "4.12.3"
+    , "body-parser": "1.12.3"
+    , "method-override": "2.3.2"
+    , "errorhandler": "1.3.5"
+    , "mongoose": "4.0.1" 
+    , "async": "0.9.0"
     , "jade": "0.29.0"
     , "jade": "0.29.0"
+    , "sequelize": "2.0.6"
+    , "mysql": "2.6.2"
   }
   }
 }
 }

+ 2 - 10
frameworks/JavaScript/express/setup.sh

@@ -3,16 +3,8 @@
 sed -i 's|mongodb://.*/hello_world|mongodb://'"${DBHOST}"'/hello_world|g' app.js
 sed -i 's|mongodb://.*/hello_world|mongodb://'"${DBHOST}"'/hello_world|g' app.js
 sed -i 's|localhost|'"${DBHOST}"'|g' app.js
 sed -i 's|localhost|'"${DBHOST}"'|g' app.js
 
 
-fw_depends nodejs
+fw_depends nvm nodejs
 
 
+# run app
 npm install
 npm install
 node app &
 node app &
-
-# !DO NOT REMOVE!
-#
-# It takes `node app` a few seconds to turn on and 
-# then fork. If you remove this sleep, the parent shell 
-# executing this script will be terminated before the 
-# application has time to awaken and be forked, and 
-# express will fail to be started
-sleep 5

+ 2 - 2
frameworks/JavaScript/hapi/README.md

@@ -12,8 +12,8 @@ This is the Hapi portion of a [benchmarking test suite](../) comparing a variety
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 The tests were run with:
 The tests were run with:
-* [Node.js v0.10.0](http://nodejs.org/)
-* [Hapi 1.2.0](http://spumko.github.io/)
+* [Node.js v0.12.2](http://nodejs.org/)
+* [Hapi 8.4.0](http://hapijs.com/)
 
 
 ## Resources
 ## Resources
 * http://nodejs.org/api/cluster.html
 * http://nodejs.org/api/cluster.html

+ 73 - 38
frameworks/JavaScript/hapi/app.js

@@ -4,12 +4,11 @@
 
 
 var cluster = require('cluster'),
 var cluster = require('cluster'),
 	numCPUs = require('os').cpus().length,
 	numCPUs = require('os').cpus().length,
-	windows = require('os').platform() == 'win32',
 	Hapi = require('hapi'),
 	Hapi = require('hapi'),
+	Sequelize = require('sequelize'),
 	mongoose = require('mongoose'),
 	mongoose = require('mongoose'),
-	async = require('async'),
 	conn = mongoose.connect('mongodb://localhost/hello_world'),
 	conn = mongoose.connect('mongodb://localhost/hello_world'),
-	connMap = { user: 'benchmarkdbuser', password: 'benchmarkdbpass', database: 'hello_world', host: 'localhost', charset: 'utf8' };
+	async = require('async');
 
 
 var WorldSchema = new mongoose.Schema({
 var WorldSchema = new mongoose.Schema({
 		id          : Number,
 		id          : Number,
@@ -19,12 +18,34 @@ var WorldSchema = new mongoose.Schema({
 	}),
 	}),
 	MWorld = conn.model('World', WorldSchema);
 	MWorld = conn.model('World', WorldSchema);
 
 
-if (!windows) {
-	var Mapper = require('mapper');
-	Mapper.connect(connMap, {verbose: false, strict: false});
-	var World = Mapper.map('World', 'id', 'randomNumber');
-	var Fortune = Mapper.map('Fortune', 'id', 'message');
-}
+var sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+	host: 'localhost',
+	dialect: 'mysql',
+	logging: false
+});
+
+var World = sequelize.define('World', {
+	id: {
+		type: 'Sequelize.INTEGER'
+	},
+	randomNumber: {
+		type: 'Sequelize.INTEGER'
+	}
+}, {
+	timestamps: false,
+	freezeTableName: true
+});
+var Fortune = sequelize.define('Fortune', {
+	id: {
+		type: 'Sequelize.INTEGER'
+	},
+	message: {
+		type: 'Sequelize.STRING'
+	}
+}, {
+	timestamps: false,
+	freezeTableName: true
+});
 
 
 if (cluster.isMaster) {
 if (cluster.isMaster) {
 	// Fork workers.
 	// Fork workers.
@@ -36,28 +57,38 @@ if (cluster.isMaster) {
 		console.log('worker ' + worker.pid + ' died');
 		console.log('worker ' + worker.pid + ' died');
 	});
 	});
 } else {
 } else {
-	var server = module.exports = Hapi.createServer(null, 8080, {
-		views: {
-			engines: {
-				handlebars: 'handlebars'
-			},
-			path: __dirname + '/views'
-		}
+	var server = module.exports = new Hapi.Server();
+	server.connection({port: 8080});
+	server.views({
+		engines: {
+			html: require('handlebars')
+		},
+		path: __dirname + '/views'
 	});
 	});
 
 
 	server.route({
 	server.route({
 		method: 'GET',
 		method: 'GET',
 		path: '/json',
 		path: '/json',
-		handler: function(req) {
-			req.reply({ message: 'Hello, World!' })
+		handler: function(req, reply) {
+			reply({ message: 'Hello, World!' }).header('Server', 'hapi');
+		}
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/plaintext',
+		handler: function(req, reply) {
+			reply('Hello, World!')
+			 .header('Server', 'hapi')
+			 .header('Content-Type', 'text/plain');
 		}
 		}
 	});
 	});
 
 
 	server.route({
 	server.route({
 		method: 'GET',
 		method: 'GET',
 		path: '/mongoose/{queries?}',
 		path: '/mongoose/{queries?}',
-		handler: function(req){
-			var queries = req.params.queries || 1,
+		handler: function(req, reply){
+			var queries = isNaN(req.params.queries) ? 1 : parseInt(req.params.queries, 10),
 				queryFunctions = [];
 				queryFunctions = [];
 
 
 			queries = Math.min(Math.max(queries, 1), 500);
 			queries = Math.min(Math.max(queries, 1), 500);
@@ -69,10 +100,10 @@ if (cluster.isMaster) {
 			}
 			}
 
 
 			async.parallel(queryFunctions, function(err, results){
 			async.parallel(queryFunctions, function(err, results){
-				if (queries == 1) {
+				if (!req.params.queries) {
 					results = results[0];
 					results = results[0];
 				}
 				}
-				req.reply(results).header('Server', 'hapi');
+				reply(results).header('Server', 'hapi');
 			});
 			});
 		}
 		}
 	});
 	});
@@ -81,8 +112,6 @@ if (cluster.isMaster) {
 		method: 'GET',
 		method: 'GET',
 		path: '/mysql-orm/{queries?}',
 		path: '/mysql-orm/{queries?}',
 		handler: function(req, reply){
 		handler: function(req, reply){
-			if (windows) return req.reply(Hapi.error.internal('Not supported on windows'));
-
 			var queries = isNaN(req.params.queries) ? 1 : parseInt(req.params.queries, 10),
 			var queries = isNaN(req.params.queries) ? 1 : parseInt(req.params.queries, 10),
 				queryFunctions = [];
 				queryFunctions = [];
 
 
@@ -90,7 +119,11 @@ if (cluster.isMaster) {
 
 
 			for (var i = 1; i <= queries; i++) {
 			for (var i = 1; i <= queries; i++) {
 				queryFunctions.push(function(callback){
 				queryFunctions.push(function(callback){
-					World.findById(Math.floor(Math.random() * 10000) + 1, callback);
+					World.findOne({
+						where: {
+							id: Math.floor(Math.random() * 10000) + 1}
+						}
+					).complete(callback);
 				});
 				});
 			}
 			}
 
 
@@ -106,10 +139,8 @@ if (cluster.isMaster) {
 	server.route({
 	server.route({
 		method: 'GET',
 		method: 'GET',
 		path: '/fortune',
 		path: '/fortune',
-		handler: function(req){
-			if (windows) return req.reply(Hapi.error.internal('Not supported on windows'));
-
-			Fortune.all(function(err, fortunes){
+		handler: function(req,reply){
+			Fortune.findAll().complete(function(err, fortunes){
 				fortunes.push({
 				fortunes.push({
 					id: 0,
 					id: 0,
 					message: 'Additional fortune added at request time.'
 					message: 'Additional fortune added at request time.'
@@ -118,7 +149,7 @@ if (cluster.isMaster) {
 					return (a.message < b.message) ? -1 : 1;
 					return (a.message < b.message) ? -1 : 1;
 				});
 				});
 
 
-				req.reply.view('fortunes.handlebars', {
+				reply.view('fortunes', {
 					fortunes: fortunes
 					fortunes: fortunes
 				}).header('Server', 'hapi');
 				}).header('Server', 'hapi');
 			});
 			});
@@ -128,8 +159,8 @@ if (cluster.isMaster) {
 	server.route({
 	server.route({
 		method: 'GET',
 		method: 'GET',
 		path: '/mongoose-update/{queries?}',
 		path: '/mongoose-update/{queries?}',
-		handler: function(req){
-			var queries = req.params.queries || 1,
+		handler: function(req, reply){
+			var queries = isNaN(req.params.queries) ? 1 : parseInt(req.params.queries, 10),
 				selectFunctions = [];
 				selectFunctions = [];
 
 
 			queries = Math.max(Math.min(queries, 500), 1);
 			queries = Math.max(Math.min(queries, 500), 1);
@@ -157,7 +188,7 @@ if (cluster.isMaster) {
 				}
 				}
 
 
 				async.parallel(updateFunctions, function(err, updates) {
 				async.parallel(updateFunctions, function(err, updates) {
-					req.reply(worlds).header('Server', 'hapi');
+					reply(worlds).header('Server', 'hapi');
 				});
 				});
 			});
 			});
 		}		
 		}		
@@ -166,15 +197,19 @@ if (cluster.isMaster) {
 	server.route({
 	server.route({
 		method: 'GET',
 		method: 'GET',
 		path: '/mysql-orm-update/{queries?}',
 		path: '/mysql-orm-update/{queries?}',
-		handler: function(req){
-			var queries = req.params.queries || 1,
+		handler: function(req,reply){
+			var queries = isNaN(req.params.queries) ? 1 : parseInt(req.params.queries, 10),
 				selectFunctions = [];
 				selectFunctions = [];
 
 
 			queries = Math.max(Math.min(queries, 500), 1);
 			queries = Math.max(Math.min(queries, 500), 1);
 
 
 			for (var i = 1; i <= queries; i++) {
 			for (var i = 1; i <= queries; i++) {
 				selectFunctions.push(function(callback){
 				selectFunctions.push(function(callback){
-					World.findById(Math.floor(Math.random() * 10000) + 1, callback);
+					World.findOne({
+						where: {
+							id: Math.floor(Math.random() * 10000) + 1}
+						}
+					).complete(callback);
 				});
 				});
 			}
 			}
 
 
@@ -185,13 +220,13 @@ if (cluster.isMaster) {
 					(function(i){
 					(function(i){
 						updateFunctions.push(function(callback){
 						updateFunctions.push(function(callback){
 							worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
 							worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
-							World.save(worlds[i], callback);
+							worlds[i].save().complete(callback);
 						});
 						});
 					})(i);
 					})(i);
 				}
 				}
 
 
 				async.parallel(updateFunctions, function(err, updates) {
 				async.parallel(updateFunctions, function(err, updates) {
-					req.reply(worlds).header('Server', 'hapi');
+					reply(worlds).header('Server', 'hapi');
 				});
 				});
 			});
 			});
 		}
 		}

+ 1 - 0
frameworks/JavaScript/hapi/benchmark_config.json

@@ -4,6 +4,7 @@
     "default": {
     "default": {
       "setup_file": "setup",
       "setup_file": "setup",
       "json_url": "/json",
       "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",

+ 6 - 5
frameworks/JavaScript/hapi/package.json

@@ -3,10 +3,11 @@
 	"version": "0.0.1",
 	"version": "0.0.1",
 	"private": true,
 	"private": true,
 	"dependencies": {
 	"dependencies": {
-		"hapi": "1.2.0",
-		"mongoose": "3.5.5",
-		"async": "0.2.5",
-		"mapper": "0.2.4-pre",
-		"handlebars": "1.0.11"
+		"hapi": "8.4.0",
+		"mongoose": "4.0.1",
+		"async": "0.9.0",
+		"handlebars": "3.0.1",
+		"sequelize": "2.0.6",
+		"mysql": "2.6.2"
 	}
 	}
 }
 }

+ 1 - 10
frameworks/JavaScript/hapi/setup.sh

@@ -2,16 +2,7 @@
 
 
 sed -i 's|localhost|'"${DBHOST}"'|g' app.js
 sed -i 's|localhost|'"${DBHOST}"'|g' app.js
 
 
-fw_depends nodejs
+fw_depends nvm nodejs
 
 
 npm install
 npm install
 node app &
 node app &
-
-# !DO NOT REMOVE!
-#
-# It takes `node app` a few seconds to turn on and 
-# then fork. If you remove this sleep, the parent shell 
-# executing this script will be terminated before the 
-# application has time to awaken and be forked, and 
-# express will fail to be started
-sleep 5

+ 0 - 0
frameworks/JavaScript/hapi/views/fortunes.handlebars → frameworks/JavaScript/hapi/views/fortunes.html


+ 0 - 0
frameworks/JavaScript/koa/README.md


+ 133 - 0
frameworks/JavaScript/koa/app.js

@@ -0,0 +1,133 @@
+var cluster = require('cluster')
+  , numCPUs = require('os').cpus().length;
+
+// Koa Deps
+var koa = require('koa')
+  , route = require('koa-route')
+  , handlebars = require('koa-handlebars')
+  , bodyParser = require('koa-bodyparser')
+  , override = require('koa-override');
+
+// Monk MongoDB Driver Deps
+var monk = require('monk')
+  , wrap = require('co-monk')
+  , db = monk('mongodb://localhost/hello_world')
+  , worlds = wrap(db.get('world'))
+  , fortunes = wrap(db.get('fortune'));
+
+if (cluster.isMaster) {
+  // Fork workers.
+  for (var i = 0; i < numCPUs; i++) {
+    cluster.fork();
+  }
+
+  cluster.on('exit', function(worker, code, signal) {
+    console.log('worker ' + worker.process.pid + ' died');
+  });
+} else {
+  var app = module.exports = koa();
+  app.use(bodyParser());
+  app.use(override());
+  app.use(handlebars({
+    // needed, otherwise missing dir err
+    partialsDir: "views"
+  }));
+
+  // routes
+  app.use(route.get('/json', jsonHandler));
+  app.use(route.get('/db', dbHandler));
+  app.use(route.get('/queries', queriesHandler));
+  app.use(route.get('/fortunes', fortuneHandler));
+  app.use(route.get('/updates', updateHandler));
+  app.use(route.get('/plaintext', textHandler));
+
+  // Helpers
+  function getRandomNumber() {
+    return Math.floor(Math.random()*10000) + 1;
+  };
+
+  function validateParam(param) {
+    var numOfQueries = isNaN(param) ? 1 : param;
+    if (numOfQueries > 500) {
+      numOfQueries = 500;
+    } else if (numOfQueries < 1) {
+      numOfQueries = 1;
+    }
+    return numOfQueries;
+  }
+
+  // Query Helpers
+  function *worldUpdateQuery() {
+    var randomId = getRandomNumber();
+    var randomNumber = getRandomNumber();
+    var result = yield worlds.update(
+      {id: randomId}, 
+      {$set: {randomNumber: randomNumber}}
+    );
+    return {
+      id: randomId,
+      randomNumber: randomNumber
+    }
+  }
+
+  function *worldQuery() {
+    return yield worlds.findOne({id: getRandomNumber()}, '-_id');
+  }
+
+  function *fortunesQuery() {
+    return yield fortunes.find({}, '-_id');
+  }
+
+  // Route handlers
+  function *jsonHandler() {
+    this.set('Server', 'Koa');
+    this.body = {
+      message: "Hello, world!"
+    }
+  }
+
+  function *dbHandler() {
+    this.set('Server', 'Koa');
+    this.body = yield worldQuery;
+  }
+
+  function *queriesHandler() {
+    this.set('Server', 'Koa');
+    var numOfQueries = validateParam(this.query.queries);
+    var queries = [];
+    for (var i = 0; i < numOfQueries; i++) {
+      queries.push(worldQuery);
+    }
+    this.body = yield queries;
+  }
+
+  function *fortuneHandler() {
+    this.set('Server', 'Koa');
+    var fortunes = yield fortunesQuery;
+    fortunes.push({
+      id: 0,
+      message: 'Additional fortune added at request time.'
+    });
+    fortunes.sort(function(a, b) {
+      return a.message < b.message ? -1 : 1;
+    });
+    yield this.render("fortunes", {fortunes: fortunes});
+  }
+
+  function *updateHandler() {
+    this.set('Server', 'Koa');
+    var numOfUpdates = validateParam(this.query.queries);
+    var queries = [];
+    for (var i = 0; i < numOfUpdates; i++) {
+      queries.push(worldUpdateQuery);
+    }
+    this.body = yield queries;
+  }
+
+  function *textHandler() {
+    this.set('Server', 'Koa');
+    this.body = 'Hello, world!';
+  }
+
+  app.listen(8080);
+}

+ 28 - 0
frameworks/JavaScript/koa/benchmark_config.json

@@ -0,0 +1,28 @@
+{
+  "framework": "koa",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MongoDB",
+      "framework": "koa",
+      "language": "JavaScript",
+      "orm": "Raw",
+      "platform": "nodejs",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "koa",
+      "notes": "",
+      "versus": "node"
+    }
+  }]
+}

+ 17 - 0
frameworks/JavaScript/koa/package.json

@@ -0,0 +1,17 @@
+{
+  "name": "koa-tfb",
+  "version": "0.0.1",
+  "description": "Koa tests for TechEmpower Framework Benchmarks.",
+  "main": "app.js",
+  "private": true,
+  "dependencies": {
+    "co-monk": "1.0.0",
+    "koa": "0.18.1",
+    "koa-bodyparser": "1.4.1",
+    "koa-handlebars": "0.5.2",
+    "koa-mongo": "0.3.0",
+    "koa-override": "1.0.0",
+    "koa-route": "2.4.0",
+    "monk": "1.0.1"
+  }
+}

+ 9 - 0
frameworks/JavaScript/koa/setup.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+fw_depends nvm nodejs
+
+sed -i 's|mongodb://.*/hello_world|mongodb://'"${DBHOST}"'/hello_world|g' app.js
+
+# run app
+npm install
+node --harmony app &

+ 0 - 0
frameworks/JavaScript/koa/source_code


+ 21 - 0
frameworks/JavaScript/koa/views/fortunes.hbs

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

+ 16 - 5
frameworks/JavaScript/nodejs/README.md

@@ -12,23 +12,31 @@ This is the NodeJS portion of a [benchmarking test suite](../) comparing a varie
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 The tests were run with:
 The tests were run with:
-* [Node.js v0.10.0](http://nodejs.org/)
-* [Mongoose 3.5.5](http://mongoosejs.com/)
-* [Mapper 0.2.4-pre](https://github.com/mgutz/mapper)
-* [MySQL 5.5.29](https://dev.mysql.com/)
+* [Node.js v0.12.2](http://nodejs.org/)
+* [Mongoose 4.0.1](http://mongoosejs.com/)
+* [Sequelize 2.0.6](https://github.com/sequelize/sequelize)
+* [Node MySQL 2.6.2](https://github.com/felixge/node-mysql/)
+* [Node MongoDB Driver 2.0.27](https://github.com/mongodb/node-mongodb-native)
 
 
 ## Test URLs
 ## Test URLs
 ### JSON Encoding Test
 ### JSON Encoding Test
 
 
 http://localhost:8080/json
 http://localhost:8080/json
 
 
+### Plaintext Test
+
+http://localhost:8080/plaintext
+
 ### Data-Store/Database Mapping Test
 ### Data-Store/Database Mapping Test
 
 
 MongoDB:
 MongoDB:
 http://localhost:8080/mongoose
 http://localhost:8080/mongoose
 
 
+MongoDB Raw:
+http://localhost:8080/mongodb
+
 MySQL:
 MySQL:
-http://localhost:8080/sequelize
+http://localhost:8080/mysql-orm
 
 
 MySQL Raw:
 MySQL Raw:
 http://localhost:8080/mysql
 http://localhost:8080/mysql
@@ -38,6 +46,9 @@ http://localhost:8080/mysql
 MongoDB:
 MongoDB:
 http://localhost:8080/mongoose?queries=2
 http://localhost:8080/mongoose?queries=2
 
 
+MongoDB Raw:
+http://localhost:8080/mongodb?queries=2
+
 MySQL:
 MySQL:
 http://localhost:8080/mysql-orm?queries=2
 http://localhost:8080/mysql-orm?queries=2
 
 

+ 6 - 4
frameworks/JavaScript/nodejs/benchmark_config.json

@@ -24,6 +24,7 @@
       "setup_file": "setup",
       "setup_file": "setup",
       "db_url": "/mongoose",
       "db_url": "/mongoose",
       "query_url": "/mongoose?queries=",
       "query_url": "/mongoose?queries=",
+      "update_url": "/mongoose-update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
@@ -41,9 +42,9 @@
     },
     },
     "mongodb-raw": {
     "mongodb-raw": {
       "setup_file": "setup",
       "setup_file": "setup",
-      "db_url": "/mongodbdriver",
-      "query_url": "/mongodbdriver?queries=",
-      "update_url": "/update-mongodb?queries=",
+      "db_url": "/mongodb",
+      "query_url": "/mongodb?queries=",
+      "update_url": "/mongodb-update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
@@ -63,6 +64,7 @@
       "setup_file": "setup",
       "setup_file": "setup",
       "db_url": "/mysql-orm",
       "db_url": "/mysql-orm",
       "query_url": "/mysql-orm?queries=",
       "query_url": "/mysql-orm?queries=",
+      "update_url": "/mysql-orm-update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
@@ -82,7 +84,7 @@
       "setup_file": "setup",
       "setup_file": "setup",
       "db_url": "/mysql",
       "db_url": "/mysql",
       "query_url": "/mysql?queries=",
       "query_url": "/mysql?queries=",
-      "update_url": "/update?queries=",
+      "update_url": "/mysql-update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",

+ 285 - 213
frameworks/JavaScript/nodejs/hello.js

@@ -1,279 +1,351 @@
+/**
+ * Module dependencies.
+ */
+
 var cluster = require('cluster')
 var cluster = require('cluster')
   , numCPUs = require('os').cpus().length
   , numCPUs = require('os').cpus().length
-  , windows = require('os').platform() == 'win32';
-
-if(cluster.isMaster) {
-  // Fork workers.
-  for (var i = 0; i < numCPUs; i++) {
-    cluster.fork();
-  }
-
-  cluster.on('exit', function(worker, code, signal) {
-    console.log('worker ' + worker.pid + ' died');
-  });
-
-  return;
-}
-
-var http = require('http')
+  , http = require('http')
   , url = require('url')
   , url = require('url')
+  , Sequelize = require('sequelize')
+  , mysql = require('mysql')
   , async = require('async')
   , async = require('async')
   , mongoose = require('mongoose')
   , mongoose = require('mongoose')
   , conn = mongoose.connect('mongodb://localhost/hello_world')
   , conn = mongoose.connect('mongodb://localhost/hello_world')
-  , MongoClient = require('mongodb').MongoClient
-  , connMap = { user: 'benchmarkdbuser', password: 'benchmarkdbpass', database: 'hello_world', host: 'localhost' };
-
-if (!windows) {
-  var Mapper = require('mapper')  
-    , libmysql = require('mysql-libmysqlclient').createConnectionSync();
-    
-    Mapper.connect(connMap, {verbose: false, strict: false});
-    var World = Mapper.map("World", "id", "randomNumber")
-    libmysql.connectSync('localhost', 'benchmarkdbuser', 'benchmarkdbpass', 'hello_world');
-}
+  , MongoClient = require('mongodb').MongoClient;
 
 
+// MongoDB Raw Setup
 var collection = null;
 var collection = null;
-
 MongoClient.connect('mongodb://localhost/hello_world?maxPoolSize=5', function(err, db) {
 MongoClient.connect('mongodb://localhost/hello_world?maxPoolSize=5', function(err, db) {
   collection = db.collection('world');
   collection = db.collection('world');
 });
 });
 
 
-// define model
-var Schema = mongoose.Schema
-  , ObjectId = Schema.ObjectId;
+// MySQL Raw Setup
+var connection = mysql.createConnection({
+  host     : 'localhost',
+  user     : 'benchmarkdbuser',
+  password : 'benchmarkdbpass',
+  database : 'hello_world'
+});
+connection.connect();
+
+// Mongoose Setup
+var WorldSchema = new mongoose.Schema({
+    id          : Number,
+    randomNumber: Number
+  }, {
+    collection: 'world'
+  }),
+  MWorld = conn.model('World', WorldSchema);
+
+// Sequelize Setup
+var sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'localhost',
+  dialect: 'mysql',
+  logging: false
+});
 
 
-var WorldSchema = new Schema({
-    id                           : Number
-  , randomNumber                 : Number
-}, { collection : 'world' });
-var MWorld = conn.model('World', WorldSchema);
+var World = sequelize.define('World', {
+  id: {
+    type: 'Sequelize.INTEGER'
+  },
+  randomNumber: {
+    type: 'Sequelize.INTEGER'
+  }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+var Fortune = sequelize.define('Fortune', {
+  id: {
+    type: 'Sequelize.INTEGER'
+  },
+  message: {
+    type: 'Sequelize.STRING'
+  }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
 
 
+// Helper functions
 function getRandomNumber() {
 function getRandomNumber() {
   return Math.floor(Math.random() * 10000) + 1;
   return Math.floor(Math.random() * 10000) + 1;
 }
 }
 
 
+// Mongoose Query Functions
 function mongooseQuery(callback) {
 function mongooseQuery(callback) {
-  MWorld.findOne({ id: getRandomNumber()}).exec(function (err, world) {
+  MWorld.findOne({
+    id: getRandomNumber()
+  }).exec(function (err, world) {
     callback(err, world);
     callback(err, world);
   });
   });
 }
 }
 
 
+// MongoDB-Raw Query Functions
 function mongodbDriverQuery(callback) {
 function mongodbDriverQuery(callback) {
-  collection.findOne({ id: getRandomNumber()}, function(err, world) {
+  collection.findOne({
+    id: getRandomNumber()
+  }, function(err, world) {
+    world._id = undefined; // remove _id from query response
     callback(err, world);
     callback(err, world);
   });
   });
 }
 }
 
 
 function mongodbDriverUpdateQuery(callback) {
 function mongodbDriverUpdateQuery(callback) {
-  collection.findAndModify({ id: getRandomNumber()}, [['_id','asc']], {$set: {randomNumber: getRandomNumber()}}, {}, function(err, world) {
-    callback(err, world);
+  collection.findAndModify({
+    id: getRandomNumber()
+  }, [['_id','asc']], {
+    $set: {randomNumber: getRandomNumber()}
+  }, {}, function(err, world) {
+    world.value._id = undefined; // remove _id from query response
+    callback(err, world.value);
   });
   });
 }
 }
 
 
+// Sequelize Query Functions
 function sequelizeQuery(callback) {
 function sequelizeQuery(callback) {
-  World.findById(getRandomNumber(), function (err, world) {
-    callback(null, world);
-  });
+  World.findOne({
+    where: {
+      id: Math.floor(Math.random() * 10000) + 1}
+    }
+  ).complete(callback);
 }
 }
 
 
-http.createServer(function (req, res) {
-  // JSON response object
-  var hello = {message: "Hello, World!"};
-  var helloStr = "Hello, World!";
-  var path = url.parse(req.url).pathname;
-  
-  // mysql on windows is not supported
-  if (windows && (path.substr(0, 3) == '/my' || path == '/update')) {
-    path = '/doesntexist';
-  }
-
-  switch (path) {
-  case '/json':
-    // JSON Response Test
-    res.writeHead(200, {'Content-Type': 'application/json; charset=UTF-8'});
-    // Write JSON object to response
-    res.end(JSON.stringify(hello));
-    break;
-
-  case '/plaintext':
-    // JSON Response Test
-    res.writeHead(200, {'Content-Type': 'text/plain; charset=UTF-8'});
-    // Write JSON object to response
-    res.end(helloStr);
-    break;
-
-  case '/mongodbdriver':
-    // Database Test
-    var values = url.parse(req.url, true);
-    var queries = values.query.queries || 1;
-    var queryFunctions = new Array(queries);
 
 
-    for (var i = 0; i < queries; i += 1) {
-      queryFunctions[i] = mongodbDriverQuery;
+// MySQL-Raw Query Functions
+function mysqlQuery(callback) {
+  connection.query("SELECT * FROM world WHERE id = " + getRandomNumber(), function (err, rows, fields) {
+    if (err) {
+      throw err;
     }
     }
+    callback(null, rows[0]);
+  });
+}
 
 
-    res.writeHead(200, {'Content-Type': 'application/json; charset=UTF-8'});
-
-    async.parallel(queryFunctions, function(err, results) {
-      if (queries == 1) {
-        results = results[0];
+function mysqlUpdateQuery(callback) {
+  connection.query("SELECT * FROM world WHERE id = " + getRandomNumber(), function (err, rows, fields) {
+    if (err) {
+      throw err;
+    }
+    rows[0].randomNumber = getRandomNumber();
+    var updateQuery = "UPDATE world SET randomNumber = " + rows[0].randomNumber + " WHERE id = " + rows[0]['id'];
+    connection.query(updateQuery, function (err, result) {
+      if (err) {
+        throw err;
       }
       }
-      res.end(JSON.stringify(results));
+      callback(null, rows[0]);
     });
     });
-    break;
-
-  case '/mongoose':
-    // Database Test
-    var values = url.parse(req.url, true);
-    var queries = values.query.queries || 1;
-    var queryFunctions = new Array(queries);
+  });
+} 
 
 
-    for (var i = 0; i < queries; i += 1) {
-      queryFunctions[i] = mongooseQuery;
-    }
+if(cluster.isMaster) {
+  // Fork workers.
+  for (var i = 0; i < numCPUs; i++) {
+    cluster.fork();
+  }
 
 
-    res.writeHead(200, {'Content-Type': 'application/json; charset=UTF-8'});
+  cluster.on('exit', function(worker, code, signal) {
+    console.log('worker ' + worker.pid + ' died');
+  });
 
 
-    async.parallel(queryFunctions, function(err, results) {
-      if (queries == 1) {
-        results = results[0];
-      }
-      res.end(JSON.stringify(results));
-    });
-    break;
+  return;
+} else {
+  http.createServer(function (req, res) {
+    // JSON response object
+    var hello = {message: "Hello, World!"};
+    var helloStr = "Hello, World!";
+    var path = url.parse(req.url).pathname;
+
+    switch (req.url) {
+      case '/json':
+        res.writeHead(200, {
+          'Content-Type': 'application/json',
+          'Server': 'Node'
+        });
+        res.end(JSON.stringify(hello));
+        break;
 
 
-  case '/mysql-orm':
+      case '/plaintext':
+        res.writeHead(200, {
+          'Content-Type': 'text/plain; charset=UTF-8',
+          'Server': 'Node'
+        });
+        res.end(helloStr);
+        break;
+    }
     var values = url.parse(req.url, true);
     var values = url.parse(req.url, true);
-    var queries = values.query.queries || 1;
-    var queryFunctions = new Array(queries);
+    var queries = isNaN(values.query.queries) ? 1 : parseInt(values.query.queries, 10);
+    queries = Math.min(Math.max(queries, 1), 500);
+    switch (values.pathname) {
+      // Raw MongoDB Routes
+      case '/mongodb':
+        var queryFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          queryFunctions.push(mongodbDriverQuery);
+        }
 
 
-    for (var i = 0; i < queries; i += 1) {
-      queryFunctions[i] = sequelizeQuery;
-    }
+        async.parallel(queryFunctions, function(err, results) {
+          if (!values.query.queries) {
+            results = results[0];
+          }
+          res.writeHead(200, {
+            'Content-Type': 'application/json',
+            'Server': 'Node'
+          });
+          res.end(JSON.stringify(results));
+        });
+        break;
 
 
-    res.writeHead(200, {'Content-Type': 'application/json'});
+      case '/mongodb-update':
+        var queryFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          queryFunctions.push(mongodbDriverUpdateQuery);
+        }
 
 
-    async.parallel(queryFunctions, function(err, results) {
-      if (queries == 1) {
-        results = results[0];
-      }
-      res.end(JSON.stringify(results));
-    });
-    break;
-
-  case '/mysql':
-    res.writeHead(200, {'Content-Type': 'application/json'});
-
-    function libmysqlQuery(callback) {
-      libmysql.query("SELECT * FROM world WHERE id = " + getRandomNumber(), function (err, res) {
-        if (err) {
-	        throw err;
-	      }
-	
-	      res.fetchAll(function(err, rows) {
-      	  if (err) {
-      	    throw err;
-      	  }
-
-      	  res.freeSync();
-      	  callback(null, rows[0]);
+        async.parallel(queryFunctions, function(err, results) {
+          res.writeHead(200, {
+            'Content-Type': 'application/json',
+            'Server': 'Node'
+          });
+          res.end(JSON.stringify(results));
         });
         });
-      });
-    } 
+        break;
 
 
-    var values = url.parse(req.url, true);
-    var queries = values.query.queries || 1;
-    var queryFunctions = new Array(queries);
-
-    for (var i = 0; i < queries; i += 1) {
-      queryFunctions[i] = libmysqlQuery;
-    }
-    async.parallel(queryFunctions, function(err, results) {
-      if (err) {
-        res.writeHead(500);
-        return res.end('MYSQL CONNECTION ERROR.');
-      }
-      if (queries == 1) {
-        results = results[0];
-      }
-      res.end(JSON.stringify(results));
-    });
-    break;
+      // Mongoose ORM Routes
+      case '/mongoose':
+        var queryFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          queryFunctions.push(mongooseQuery);
+        }
 
 
-  case '/update':
-    res.writeHead(200, {'Content-Type': 'application/json'});
+        async.parallel(queryFunctions, function(err, results) {
+          if (!values.query.queries) {
+            results = results[0];
+          }
+          res.writeHead(200, {
+            'Content-Type': 'application/json',
+            'Server': 'Node'
+          });
+          res.end(JSON.stringify(results));
+        });
+        break;
 
 
-    function libmysqlQuery(callback) {
-      libmysql.query("SELECT * FROM world WHERE id = " + getRandomNumber(), function (err, res) {
-        if (err) {
-          throw err;
+      case '/mongoose-update':
+        var selectFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          selectFunctions.push(mongooseQuery);
         }
         }
-  
-        res.fetchAll(function(err, rows) {
-          if (err) {
-            throw err;
+
+        async.parallel(selectFunctions, function(err, worlds) {
+          var updateFunctions = [];
+
+          for (var i = 0; i < queries; i++) {
+            (function(i){
+              updateFunctions.push(function(callback){
+                worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
+                MWorld.update({
+                  id: worlds[i]
+                }, {
+                  randomNumber: worlds[i].randomNumber
+                }, callback);
+              });
+            })(i);
           }
           }
 
 
-          res.freeSync();
+          async.parallel(updateFunctions, function(err, updates) {
+            res.writeHead(200, {
+              'Content-Type': 'application/json',
+              'Server': 'Node'
+            });
+            res.end(JSON.stringify(worlds));
+          });
+        });
+        break;
+
+      // Sequelize (MySQL ORM) Routes
+      case '/mysql-orm':
+        var queryFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          queryFunctions.push(sequelizeQuery);
+        }
 
 
-          rows[0].randomNumber = getRandomNumber();
-          libmysql.query("UPDATE World SET randomNumber = " + rows[0].randomNumber + " WHERE id = " + rows[0]['id'], function (err, res) {
-            if (err) {
-              throw err;
-            }
-            callback(null, rows[0]);
+        async.parallel(queryFunctions, function(err, results) {
+          if (!values.query.queries) {
+            results = results[0];
+          }
+          res.writeHead(200, {
+            'Content-Type': 'application/json',
+            'Server': 'Node'
           });
           });
+          res.end(JSON.stringify(results));
         });
         });
-      });
-    } 
+        break;
 
 
-    var values = url.parse(req.url, true);
-    var queries = values.query.queries || 1;
-    if(queries < 1) {
-      queries = 1;
-    } else if(queries > 500) {
-      queries = 500;
-    }
-    var queryFunctions = new Array(queries);
+      case '/mysql-orm-update':
+        var selectFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          selectFunctions.push(sequelizeQuery);
+        }
 
 
-    for (var i = 0; i < queries; i += 1) {
-      queryFunctions[i] = libmysqlQuery;
-    }
-    async.parallel(queryFunctions, function(err, results) {
-      if (err) {
-        res.writeHead(500);
-        return res.end('MYSQL CONNECTION ERROR.');
-      }
-      res.end(JSON.stringify(results));
-    });
-    break;
+        async.parallel(selectFunctions, function(err, worlds) {
+          var updateFunctions = [];
 
 
-  case '/update-mongodb':
-    // Database Test
-    var values = url.parse(req.url, true);
-    var queries = values.query.queries || 1;
-    if (queries < 1) {
-      queries = 1;
-    } else if (queries > 500) {
-      queries = 500;
-    }
+          for (var i = 0; i < queries; i++) {
+            (function(i){
+              updateFunctions.push(function(callback){
+                worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
+                worlds[i].save().complete(callback);
+              });
+            })(i);
+          }
 
 
-    var queryFunctions = new Array(queries);
+          async.parallel(updateFunctions, function(err, updates) {
+            res.writeHead(200, {
+              'Content-Type': 'application/json',
+              'Server': 'Node'
+            });
+            res.end(JSON.stringify(worlds));
+          });
+        });
+        break;
 
 
-    for (var i = 0; i < queries; i += 1) {
-      queryFunctions[i] = mongodbDriverUpdateQuery;
-    }
+      // Raw MongoDB Routes
+      case '/mysql':
+        var queryFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          queryFunctions.push(mysqlQuery);
+        }
 
 
-    res.writeHead(200, {'Content-Type': 'application/json; charset=UTF-8'});
+        async.parallel(queryFunctions, function(err, results) {
+          if (!values.query.queries) {
+            results = results[0];
+          }
+          res.writeHead(200, {
+            'Content-Type': 'application/json',
+            'Server': 'Node'
+          });
+          res.end(JSON.stringify(results));
+        });
+        break;
 
 
-    async.parallel(queryFunctions, function(err, results) {
-      res.end(JSON.stringify(results));
-    });
-    break;
+      case '/mysql-update':
+        var queryFunctions = [];
+        for (var i = 0; i < queries; i += 1) {
+          queryFunctions.push(mysqlUpdateQuery);
+        }
+        async.parallel(queryFunctions, function(err, results) {
+          res.writeHead(200, {
+            'Content-Type': 'application/json',
+            'Server': 'Node'
+          });
+          res.end(JSON.stringify(results));
+        });
+        break;
 
 
-  default:
-    // File not found handler
-    res.writeHead(501, {'Content-Type': 'text/plain; charset=UTF-8'});
-    res.end("NOT IMPLEMENTED");
-  }
-}).listen(8080);
+      default:
+        // File not found handler
+        res.writeHead(501, {'Content-Type': 'text/plain; charset=UTF-8'});
+        res.end("NOT IMPLEMENTED");
+      }
+    }).listen(8080);
+}

+ 5 - 5
frameworks/JavaScript/nodejs/package.json

@@ -3,11 +3,11 @@
   , "version": "0.0.1"
   , "version": "0.0.1"
   , "private": true
   , "private": true
   , "dependencies": {
   , "dependencies": {
-      "mongoose": "3.5.5" 
-    , "async": "0.2.5"
-    , "mysql-libmysqlclient": "1.5.2"
-    , "mapper": "0.2.4-pre"
-    , "mongodb": "1.3.0"
+      "mongoose": "4.0.1" 
+    , "async": "0.9.0"
+    , "mongodb": "2.0.27"
+    , "sequelize": "2.0.6"
+    , "mysql": "2.6.2"
   }
   }
   , "main": "hello.js"
   , "main": "hello.js"
 }
 }

+ 3 - 12
frameworks/JavaScript/nodejs/setup.sh

@@ -1,18 +1,9 @@
 #!/bin/bash
 #!/bin/bash
 
 
-sed -i 's|localhost|'"${DBHOST}"'|g' hello.js
-sed -i 's|mongodb//.*/hello_world|mongodb//'"${DBHOST}"'/hello_world|g' hello.js
+fw_depends nvm nodejs
 
 
-fw_depends nodejs
+sed -i 's|localhost|'"${DBHOST}"'|g' hello.js
+sed -i 's|mongodb://.*/hello_world|mongodb://'"${DBHOST}"'/hello_world|g' hello.js
 
 
 npm install
 npm install
 node hello.js &
 node hello.js &
-
-# !DO NOT REMOVE!
-#
-# It takes `node app` a few seconds to turn on and 
-# then fork. If you remove this sleep, the parent shell 
-# executing this script will be terminated before the 
-# application has time to awaken and be forked, and 
-# express will fail to be started
-sleep 5

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