Răsfoiți Sursa

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 ani în urmă
părinte
comite
2fd1d353be
100 a modificat fișierele cu 2432 adăugiri și 1119 ștergeri
  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
-python: 
+python:
   - "2.7"
 
 env:
@@ -9,12 +9,12 @@ env:
     # we end up starting ~200+ different workers. Seems that ~100 is the limit
     # 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)
-    #   cd frameworks 
+    #   cd frameworks
     #   find . -type d -depth 2 | sed 's|./|    - "TESTDIR=|' | sed 's/$/"/g'
     #
-    #  
+    #
     - "TESTDIR=C/lwan"
     - "TESTDIR=C/duda"
     - "TESTDIR=C/haywire"
@@ -34,6 +34,7 @@ env:
     - "TESTDIR=Clojure/http-kit"
     - "TESTDIR=Clojure/luminus"
     - "TESTDIR=Clojure/pedestal"
+    - "TESTDIR=Clojure/aleph"
     - "TESTDIR=Dart/dart"
     - "TESTDIR=Dart/dart-redstone"
     - "TESTDIR=Dart/dart-start"
@@ -82,6 +83,7 @@ env:
     - "TESTDIR=Java/wildfly-ee7"
     - "TESTDIR=JavaScript/express"
     - "TESTDIR=JavaScript/hapi"
+    - "TESTDIR=JavaScript/koa"
     - "TESTDIR=JavaScript/nodejs"
     - "TESTDIR=JavaScript/ringojs"
     - "TESTDIR=JavaScript/ringojs-convenient"
@@ -97,6 +99,7 @@ env:
     - "TESTDIR=PHP/cakephp"
     - "TESTDIR=PHP/hhvm"
     - "TESTDIR=PHP/php"
+    - "TESTDIR=PHP/cygnite-php-framework"
     - "TESTDIR=PHP/codeigniter"
     - "TESTDIR=PHP/php-fatfree"
     - "TESTDIR=PHP/fuel"
@@ -125,6 +128,8 @@ env:
     - "TESTDIR=Python/django"
     - "TESTDIR=Python/falcon"
     - "TESTDIR=Python/flask"
+    - "TESTDIR=Python/historical"
+    - "TESTDIR=Python/klein"
     - "TESTDIR=Python/pyramid"
     - "TESTDIR=Python/tornado"
     - "TESTDIR=Python/turbogears"
@@ -140,6 +145,7 @@ env:
     - "TESTDIR=Ruby/rails"
     - "TESTDIR=Ruby/rails-stripped"
     - "TESTDIR=Ruby/sinatra"
+    - "TESTDIR=Scala/colossus"
     - "TESTDIR=Scala/finagle"
     - "TESTDIR=Scala/lift-stateless"
     - "TESTDIR=Scala/plain"
@@ -149,10 +155,11 @@ env:
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/unfiltered"
+    - "TESTDIR=Scala/http4s"
     - "TESTDIR=Ur/urweb"
 
 before_install:
-  # Need to install python modules before using 
+  # Need to install python modules before using
   # python
   - pip install -r requirements.txt
 
@@ -161,15 +168,15 @@ before_install:
   - ./toolset/run-ci.py cisetup "$TESTDIR"
 
 addons:
-  postgresql: "9.3" 
+  postgresql: "9.3"
 
 install:
   # Install prerequisites
   - ./toolset/run-ci.py prereq "$TESTDIR"
-  
-  # Install software for this framework  
+
+  # Install software for this framework
   - ./toolset/run-ci.py install "$TESTDIR"
-   
-script: 
+
+script:
   # Pick one test in this directory and verify
   - 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",
       "db_url": "/compojure/db",
       "query_url": "/compojure/db/",
+      "update_url": "/compojure/updates/",
       "fortune_url": "/compojure/fortune-hiccup",
       "plaintext_url": "/compojure/plaintext",
       "port": 8080,

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

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

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

@@ -13,6 +13,7 @@
             [clojure.java.jdbc :as jdbc]
             [clojure.java.jdbc.sql :as sql]))
 
+
 ; 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"
@@ -22,6 +23,7 @@
                   :maximum-pool-size 256
                   }))
 
+
 ; Set up entity World and the database representation
 (defentity world
   (pk :id)
@@ -29,19 +31,23 @@
   (entity-fields :id :randomNumber)
   (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
     (select world
             (fields :id :randomNumber)
             (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"
 ; https://github.com/clojure/java.jdbc/blob/master/doc/clojure/java/jdbc/ConnectionPooling.md
@@ -52,6 +58,7 @@
    :user "benchmarkdbuser"
    :password "benchmarkdbpass"})
 
+
 (defn pool
   [spec]
   (let [cpds (doto (ComboPooledDataSource.)
@@ -65,35 +72,42 @@
                (.setMaxIdleTime (* 3 60 60)))]
     {:datasource cpds}))
 
+
 (def pooled-db (delay (pool db-spec-mysql-raw)))
 
+
 (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)
       ; 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-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."
-  (let [q (try (Integer/parseInt queries)
+  [queries]
+  (let [n (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
+    (cond
+      (< n 1) 1
+      (> n 500) 500
+      :else n)))
+
 
 ; Set up entity World and the database representation
 (defentity fortune
@@ -102,19 +116,27 @@
   (entity-fields :id :message)
   (database db))
 
-(defn get-all-fortunes []
+
+(defn get-all-fortunes
   "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
-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)))
 
-(defn fortunes-hiccup [fortunes]
+
+(defn fortunes-hiccup
   "Render the given fortunes to simple HTML using Hiccup."
+  [fortunes]
   (html5
    [:head
     [:title "Fortunes"]]
@@ -129,27 +151,93 @@ message text, and then return the results."
         [: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
-  (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"))
 
-; Format responses as JSON
+
 (def app
+  "Format responses as JSON"
   (wrap-json-response app-routes))

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

@@ -3,9 +3,12 @@
   "tests": [{
     "default": {
       "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,
       "approach": "Realistic",
       "classification": "Platform",

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

@@ -6,9 +6,17 @@
                  [ring/ring-json "0.2.0"]
                  [org.clojure/tools.cli "0.2.1"]
                  [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]]
-                 [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
   :aot [hello.handler]
   :uberjar-name "http-kit-standalone.jar"

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

@@ -1,72 +1,207 @@
 (ns hello.handler
   (:gen-class)
+  (:import com.mchange.v2.c3p0.ComboPooledDataSource)
   (:use compojure.core
         ring.middleware.json
         org.httpkit.server
         [clojure.tools.cli :only [cli]]
-        ring.util.response)
+        korma.db
+        korma.core
+        hiccup.core
+        hiccup.util
+        hiccup.page)
   (: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
-    (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
 (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"))
 
 
-(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
   (let [handler (wrap-json-response app-routes)
         cpu (.availableProcessors (Runtime/getRuntime))]
     ;; 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))))
 
+
 (defn -main [& args]
   (let [[options _ banner]
         (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"])]
-    (when (:help options) (println banner) (System/exit 0))
+    (when (:help options)
+          (println banner)
+          (System/exit 0))
     (start-server options)))

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

@@ -2,6 +2,8 @@
 
 fw_depends leiningen java7
 
+sed -i 's|:subname "//.*:3306|:subname "//'"${DBHOST}"':3306|g' hello/src/hello/handler.clj
+
 cd hello
 lein clean
 lein deps
@@ -10,4 +12,4 @@ rm -rf target
 lein uberjar
 # -server is much faster
 # '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": [{
     "default": {
       "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,
       "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",
       "framework": "luminus",
       "language": "Clojure",
       "orm": "Raw",
-      "platform": "Servlet",
-      "webserver": "Resin",
+      "platform": "http-kit",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "luminus",
       "notes": "",
-      "versus": "servlet"
+      "versus": ""
     }
   }]
 }

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

@@ -5,7 +5,6 @@
 pom.xml
 *.jar
 *.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
 
-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
-  {: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 %}
 
@@ -13,6 +13,6 @@
     <td>{{message.message}}</td>
   </tr>
   {% endfor %}
-</table>  
-  
+</table>
+
 {% 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
-  (: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]
             [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
   "init will be called once when
@@ -21,33 +24,34 @@
   []
   (timbre/set-config!
     [: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
-     :fn rotor/appender-fn})
+     :fn                    rotor/appender-fn})
 
   (timbre/set-config!
     [: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
   "destroy will be called when your application
    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
-  (: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
   (testing "main route"
     (let [response (app (request :get "/"))]
-      (is (= (:status response) 200))
-      (is (= (:body response)
-             "Hello, World!"))))
+      (is (= 200 (:status response)))))
 
   (testing "not-found route"
     (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 
 
-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
 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/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/public
 ./luminus/hello/resources/public/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/hello
 ./luminus/hello/test/hello/test
 ./luminus/hello/test/hello/test/handler.clj
+

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

@@ -41,11 +41,12 @@
   (database mysql-db))
 
 
-(defn random-world []
+(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 }))))
+            (where {:id id }))))
 
 
 (defn run-queries
@@ -62,26 +63,27 @@
 
 (defn sanitizeQueriesParam
   "Sanitizes the `queries` parameter. Caps the value between 1 and 500.
-Invalid (stringy) values become 1"
+  Invalid (stringy) values become 1"
   [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
   "Test 3: Multiple database queries"
   [request]
   (-> request
-    (sanitizeQueriesParam)
-    (run-queries)
-    (bootstrap/json-response)))
+      (sanitizeQueriesParam)
+      (run-queries)
+      (bootstrap/json-response)))
 
 
 ; Set up entity Fortune and the database representation
@@ -92,23 +94,26 @@ Invalid (stringy) values become 1"
   (database mysql-db))
 
 
-(defn get-all-fortunes []
+(defn get-all-fortunes
   "Query all Fortune records from the database."
+  []
   (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
-message text, and then return the results."
+  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 [fortunes]
+(defn fortunes-hiccup
   "Render the given fortunes to simple HTML using Hiccup."
+  [fortunes]
   (html5
    [:head
     [:title "Fortunes"]]
@@ -124,8 +129,9 @@ message text, and then return the results."
      ]]))
 
 
-(defn fortune-test [request]
+(defn fortune-test
   "Test 4: Fortunes"
+  [request]
   (->
     (get-fortunes)
     (fortunes-hiccup)
@@ -136,23 +142,25 @@ message text, and then return the results."
 
 (defn update-and-persist
   "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]
-  (let [results (-> request (sanitizeQueriesParam) (run-queries))]
+  (let [results (-> request
+                    (sanitizeQueriesParam)
+                    (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))
+                (set-fields {:randomNumber (:randomNumber w)})
+                (where {:id [:id w]}))))
+    results))
 
 
 (defn db-updates
   "Test 5: Database updates"
   [request]
   (-> request
-    (update-and-persist)
-    (bootstrap/json-response)))
+      (update-and-persist)
+      (bootstrap/json-response)))
 
 
 (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)
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"
 
 libraryDependencies ++= Seq(
   javaJdbc,
   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/"
 
 // 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)
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"
 
 libraryDependencies ++= Seq(
   javaJdbc,
   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)

+ 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)
 # 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
 

+ 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/"
 
 // 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"
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"
 
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 
 libraryDependencies ++= Seq(
   javaJdbc,
   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"

+ 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/"
 
 // 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)
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.6"
 
 libraryDependencies ++= Seq(
   javaJdbc,
   javaJpa,
-  "mysql" % "mysql-connector-java" % "5.1.33",
-  "org.hibernate" % "hibernate-entitymanager" % "4.3.6.Final",
-  "com.edulify" %% "play-hikaricp" % "1.5.0"
+  "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)

+ 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)
 # 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
 

+ 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/"
 
 // 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)
 
-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/"
 
 // 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
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 cd play2-java
 

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

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

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

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

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

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

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

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

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

@@ -8,8 +8,59 @@
       "fortune_url": "/fortune",
       "update_url": "/update?queries=",
       "plaintext_url": "/plaintext",
+
       "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",
       "classification": "Micro",
       "database": "MySQL",
@@ -20,7 +71,32 @@
       "webserver": "None",
       "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": "",
       "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>
 
-        <sabina-version>1.1.1</sabina-version>
+        <sabina-version>1.3.0</sabina-version>
     </properties>
 
     <repositories>
@@ -92,6 +92,11 @@
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.28</version>
         </dependency>
+        <dependency>
+            <groupId>org.mongodb</groupId>
+            <artifactId>mongo-java-driver</artifactId>
+            <version>3.0.0</version>
+        </dependency>
 
         <dependency>
             <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.view.MustacheView.renderMustache;
 
-import com.mchange.v2.c3p0.ComboPooledDataSource;
 import sabina.Request;
+import sabina.server.MatcherFilter;
 
-import java.sql.*;
 import java.util.*;
 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 CONTENT_TYPE_TEXT = "text/plain";
     private static final String CONTENT_TYPE_JSON = "application/json";
     private static final String QUERIES_PARAM = "queries";
 
-    private static Properties loadConfiguration () {
+    static Properties loadConfiguration () {
         try {
             Properties settings = new Properties ();
             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 {
-            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) {
         try {
             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) {
         it.response.type (CONTENT_TYPE_TEXT);
         return MESSAGE;
     }
 
+    private static Object getJson (Request it) {
+        it.response.type (CONTENT_TYPE_JSON);
+        return toJson (new Message ());
+    }
+
     private static void addCommonHeaders (Request it) {
         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 ("/db", Application::getDb);
         get ("/query", Application::getDb);
@@ -216,9 +132,18 @@ final class Application {
         get ("/update", Application::getUpdates);
         get ("/plaintext", Application::getPlaintext);
         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 ();
     }
+
+    @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;
 
-/**
- * TODO .
- *
- * @author jam
- */
 final class Fortune {
     final int id;
     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.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?\
 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.testng.AssertJUnit.*;
-import static sabina.benchmark.Application.main;
 import static sabina.Sabina.stop;
 
 import java.io.IOException;
@@ -39,37 +38,13 @@ import org.testng.annotations.Test;
  * <p>TODO Change assert's order
  */
 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 Gson GSON = new Gson ();
 
     @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 () {
@@ -134,6 +109,11 @@ public final class ApplicationTest {
         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)
     public void five_hundred_queries () throws IOException {
         checkDbRequest ("/query?queries=500", 500);
@@ -192,6 +172,11 @@ public final class ApplicationTest {
         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)
     public void five_hundred_updates () throws IOException {
         checkDbRequest ("/update?queries=500", 500);
@@ -218,7 +203,7 @@ public final class ApplicationTest {
         assertTrue (res.getFirstHeader ("Server") != null);
         assertTrue (res.getFirstHeader ("Date") != null);
         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) {

+ 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)
 
-### 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
 
-* [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
 
+### Plaintext Test
+
+    http://localhost:8080/plaintext
+
 ### JSON Encoding Test
 
     http://localhost:8080/json
 
 ### 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.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> {
 
-  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 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
   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.setPeriodic(1000, new Handler<Long>() {
       @Override
@@ -36,19 +88,24 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
 
   @Override
   public void handle(HttpServerRequest req) {
-    String path = req.path();
-    switch (path) {
-      case "/plaintext":
+    switch (req.path()) {
+      case PATH_PLAINTEXT:
         handlePlainText(req);
         break;
-      case "/json":
+      case PATH_JSON:
         handleJson(req);
         break;
-      case "/db":
+      case PATH_DB:
         handleDbMongo(req);
         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;
       default:
         req.response().setStatusCode(404);
@@ -60,21 +117,55 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
     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) {
     HttpServerResponse resp = req.response();
-    setHeaders(resp, "text/plain", helloWorldContentLength);
+    setHeaders(resp, RESPONSE_TYPE_PLAIN, helloWorldContentLength);
     resp.end(helloWorldBuffer);
   }
 
   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();
-    setHeaders(resp, "application/json", String.valueOf(buff.length()));
+    setHeaders(resp, RESPONSE_TYPE_JSON, String.valueOf(buff.length()));
     resp.end(buff);
   }
 
   private void handleDbMongo(final HttpServerRequest req) {
-    findRandom(ThreadLocalRandom.current(), new Handler<Message<JsonObject>>() {
+    findRandom(new Handler<Message<JsonObject>>() {
       @Override
       public void handle(Message<JsonObject> reply) {
         JsonObject world = getResultFromReply(reply);
@@ -86,72 +177,86 @@ public class WebServer extends Verticle implements Handler<HttpServerRequest> {
 
   private JsonObject getResultFromReply(Message<JsonObject> reply) {
     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;
   }
 
-  private void handleQueriesMongo(final HttpServerRequest req) {
+  private void handleDBMongo(final HttpServerRequest req, boolean randomUpdates) {
     int queriesParam = 1;
     try {
-      queriesParam = Integer.parseInt(req.params().get("queries"));
+      queriesParam = Integer.parseInt(req.params().get(TEXT_QUERIES));
     } catch (NumberFormatException e) {
-      e.printStackTrace();
-    }
-    if (queriesParam < 1)
-    {
       queriesParam = 1;
     }
-    if (queriesParam > 500)
-    {
+    if (queriesParam < 1) {
+      queriesParam = 1;
+    } else if (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++) {
-      findRandom(random, dbh);
+      findRandom(dbh);
     }
   }
 
-  private void findRandom(Random random, Handler<Message<JsonObject>> handler) {
+  private void findRandom(Handler<Message<JsonObject>> handler) {
     vertx.eventBus().send(
-        "hello.persistor",
+        MONGO_ADDRESS,
         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);
   }
 
+  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) {
     Buffer buff = new Buffer(result);
     HttpServerResponse resp = req.response();
-    setHeaders(resp, "application/json", String.valueOf(buff.length()));
+    setHeaders(resp, RESPONSE_TYPE_JSON, String.valueOf(buff.length()));
     resp.end(buff);
   }
 
   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 int queries;
     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;
       queries = queriesParam;
+      randomUpdates = performRandomUpdates;
+      random = ThreadLocalRandom.current();
       worlds = new JsonArray();
     }
-
     @Override
     public void handle(Message<JsonObject> reply) {
       JsonObject world = getResultFromReply(reply);
+      if (randomUpdates) {
+        world.putValue(RANDOM_NUMBER, (random.nextInt(10000) + 1));
+        updateRandom(world);        
+      }
       worlds.add(world);
       if (worlds.size() == this.queries) {
         // 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 = {
   address: 'hello.persistor',
   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) {

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

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

+ 1 - 1
frameworks/Java/vertx/source_code

@@ -1,2 +1,2 @@
-./vertx/App.groovy
+./vertx/app.js
 ./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
 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
 * http://nodejs.org/api/cluster.html

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

@@ -5,28 +5,56 @@
 
 var cluster = require('cluster')
   , numCPUs = require('os').cpus().length
-  , windows = require('os').platform() == 'win32'
   , express = require('express')
+  , Sequelize = require('sequelize')
   , mongoose = require('mongoose')
-  , async = require('async')
   , 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
   , 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) {
   // Fork workers.
@@ -41,95 +69,104 @@ if (cluster.isMaster) {
   var app = module.exports = express();
 
   // 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
 
   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) {
-    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++ ) {
       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) {
-      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)
-      , worlds  = []
       , queryFunctions = [];
 
     queries = Math.min(Math.max(queries, 1), 500);
 
     for (var i = 1; i <= queries; i++ ) {
       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) {
       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) {
-    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."};
       fortunes.push(newFortune);
-      fortunes.sort(sortFortunes);
+      fortunes.sort(function (a, b) {
+        return (a.message < b.message) ? -1 : 1;
+      });
 
       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) {
-    var queries = req.query.queries || 1
+    var queries = isNaN(req.query.queries) ? 1 : parseInt(req.query.queries, 10)
       , selectFunctions = [];
 
     queries = Math.min(queries, 500);
@@ -163,16 +200,18 @@ if (cluster.isMaster) {
   });
 
   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 = [];
 
-    queries = Math.min(queries, 500);
+    queries = Math.max(Math.min(queries, 500), 1);
 
     for (var i = 1; i <= queries; i++ ) {
       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){
           updateFunctions.push(function(callback){
             worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
-            World.save(worlds[i], callback);
+            worlds[i].save().complete(callback);
           });
         })(i);
       }
@@ -191,7 +230,8 @@ if (cluster.isMaster) {
       async.parallel(updateFunctions, function(err, updates) {
         res.send(worlds);
       });
-    });   
+    });  
+
   });
 
   app.listen(8080);

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

@@ -4,6 +4,7 @@
     "default": {
       "setup_file": "setup",
       "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "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"
   , "private": true
   , "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"
+    , "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|localhost|'"${DBHOST}"'|g' app.js
 
-fw_depends nodejs
+fw_depends nvm nodejs
 
+# run app
 npm install
 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
 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
 * http://nodejs.org/api/cluster.html

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

@@ -4,12 +4,11 @@
 
 var cluster = require('cluster'),
 	numCPUs = require('os').cpus().length,
-	windows = require('os').platform() == 'win32',
 	Hapi = require('hapi'),
+	Sequelize = require('sequelize'),
 	mongoose = require('mongoose'),
-	async = require('async'),
 	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({
 		id          : Number,
@@ -19,12 +18,34 @@ var WorldSchema = new mongoose.Schema({
 	}),
 	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) {
 	// Fork workers.
@@ -36,28 +57,38 @@ if (cluster.isMaster) {
 		console.log('worker ' + worker.pid + ' died');
 	});
 } 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({
 		method: 'GET',
 		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({
 		method: 'GET',
 		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 = [];
 
 			queries = Math.min(Math.max(queries, 1), 500);
@@ -69,10 +100,10 @@ if (cluster.isMaster) {
 			}
 
 			async.parallel(queryFunctions, function(err, results){
-				if (queries == 1) {
+				if (!req.params.queries) {
 					results = results[0];
 				}
-				req.reply(results).header('Server', 'hapi');
+				reply(results).header('Server', 'hapi');
 			});
 		}
 	});
@@ -81,8 +112,6 @@ if (cluster.isMaster) {
 		method: 'GET',
 		path: '/mysql-orm/{queries?}',
 		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),
 				queryFunctions = [];
 
@@ -90,7 +119,11 @@ if (cluster.isMaster) {
 
 			for (var i = 1; i <= queries; i++) {
 				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({
 		method: 'GET',
 		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({
 					id: 0,
 					message: 'Additional fortune added at request time.'
@@ -118,7 +149,7 @@ if (cluster.isMaster) {
 					return (a.message < b.message) ? -1 : 1;
 				});
 
-				req.reply.view('fortunes.handlebars', {
+				reply.view('fortunes', {
 					fortunes: fortunes
 				}).header('Server', 'hapi');
 			});
@@ -128,8 +159,8 @@ if (cluster.isMaster) {
 	server.route({
 		method: 'GET',
 		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 = [];
 
 			queries = Math.max(Math.min(queries, 500), 1);
@@ -157,7 +188,7 @@ if (cluster.isMaster) {
 				}
 
 				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({
 		method: 'GET',
 		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 = [];
 
 			queries = Math.max(Math.min(queries, 500), 1);
 
 			for (var i = 1; i <= queries; i++) {
 				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){
 						updateFunctions.push(function(callback){
 							worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
-							World.save(worlds[i], callback);
+							worlds[i].save().complete(callback);
 						});
 					})(i);
 				}
 
 				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": {
       "setup_file": "setup",
       "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",

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

@@ -3,10 +3,11 @@
 	"version": "0.0.1",
 	"private": true,
 	"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
 
-fw_depends nodejs
+fw_depends nvm nodejs
 
 npm install
 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
 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
 ### JSON Encoding Test
 
 http://localhost:8080/json
 
+### Plaintext Test
+
+http://localhost:8080/plaintext
+
 ### Data-Store/Database Mapping Test
 
 MongoDB:
 http://localhost:8080/mongoose
 
+MongoDB Raw:
+http://localhost:8080/mongodb
+
 MySQL:
-http://localhost:8080/sequelize
+http://localhost:8080/mysql-orm
 
 MySQL Raw:
 http://localhost:8080/mysql
@@ -38,6 +46,9 @@ http://localhost:8080/mysql
 MongoDB:
 http://localhost:8080/mongoose?queries=2
 
+MongoDB Raw:
+http://localhost:8080/mongodb?queries=2
+
 MySQL:
 http://localhost:8080/mysql-orm?queries=2
 

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

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

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

@@ -1,279 +1,351 @@
+/**
+ * Module dependencies.
+ */
+
 var cluster = require('cluster')
   , 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')
+  , Sequelize = require('sequelize')
+  , mysql = require('mysql')
   , async = require('async')
   , mongoose = require('mongoose')
   , 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;
-
 MongoClient.connect('mongodb://localhost/hello_world?maxPoolSize=5', function(err, db) {
   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() {
   return Math.floor(Math.random() * 10000) + 1;
 }
 
+// Mongoose Query Functions
 function mongooseQuery(callback) {
-  MWorld.findOne({ id: getRandomNumber()}).exec(function (err, world) {
+  MWorld.findOne({
+    id: getRandomNumber()
+  }).exec(function (err, world) {
     callback(err, world);
   });
 }
 
+// MongoDB-Raw Query Functions
 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);
   });
 }
 
 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) {
-  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 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"
   , "private": true
   , "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"
 }

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

@@ -1,18 +1,9 @@
 #!/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
 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

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff