Browse Source

Merge branch 'master' of https://github.com/TechEmpower/FrameworkBenchmarks

haleyyoung 11 years ago
parent
commit
96dc50a1e4
100 changed files with 2198 additions and 353 deletions
  1. 54 4
      .gitignore
  2. 4 4
      HttpListener/setup.py
  3. 9 9
      README.md
  4. 14 0
      UrWeb/README.md
  5. 0 0
      UrWeb/__init__.py
  6. BIN
      UrWeb/bench.exe
  7. 81 0
      UrWeb/bench.ur
  8. 13 0
      UrWeb/bench.urp
  9. 6 0
      UrWeb/bench.urs
  10. 26 0
      UrWeb/benchmark_config
  11. 28 0
      UrWeb/create-postgres.sql
  12. 14 0
      UrWeb/fortune.sql
  13. 25 0
      UrWeb/meta/LICENSE
  14. 76 0
      UrWeb/meta/eq.ur
  15. 44 0
      UrWeb/meta/eq.urs
  16. 144 0
      UrWeb/meta/html.ur
  17. 29 0
      UrWeb/meta/html.urs
  18. 40 0
      UrWeb/meta/incl.ur
  19. 22 0
      UrWeb/meta/incl.urs
  20. 339 0
      UrWeb/meta/json.ur
  21. 31 0
      UrWeb/meta/json.urs
  22. 12 0
      UrWeb/meta/lib.urp
  23. 38 0
      UrWeb/meta/mem.ur
  24. 15 0
      UrWeb/meta/mem.urs
  25. 5 0
      UrWeb/meta/parse.ur
  26. 18 0
      UrWeb/meta/record.ur
  27. 14 0
      UrWeb/meta/record.urs
  28. 75 0
      UrWeb/meta/sql.ur
  29. 47 0
      UrWeb/meta/sql.urs
  30. 145 0
      UrWeb/meta/variant.ur
  31. 83 0
      UrWeb/meta/variant.urs
  32. 27 0
      UrWeb/setup.py
  33. 1 0
      UrWeb/source_code
  34. 3 0
      UrWeb/world.sql
  35. 4 4
      aspnet-stripped/setup_iis.py
  36. 4 4
      aspnet/setup_iis.py
  37. 10 10
      aspnet/setup_nginx.py
  38. 6 6
      aspnet/setup_xsp.py
  39. 8 8
      beego/setup.py
  40. 1 1
      beego/src/hello/hello.go
  41. 31 0
      benchmark.cfg.example
  42. 3 3
      bottle/setup.py
  43. 6 6
      bottle/setup_nginxuwsgi.py
  44. 3 3
      bottle/setup_py3.py
  45. 3 3
      bottle/setup_pypy.py
  46. 11 11
      cake/setup.py
  47. 7 7
      compojure/setup.py
  48. 2 2
      config/benchmark_profile
  49. 28 0
      config/create-postgres-urweb.sql
  50. 15 15
      config/create.js
  51. 5 5
      cowboy/setup_erlang.py
  52. 4 4
      cpoll_cppsp/setup.py
  53. 4 4
      dancer/setup.py
  54. 6 6
      dart-start/setup.py
  55. 10 10
      dart-stream/server.dart
  56. 6 6
      dart-stream/setup.py
  57. 5 5
      dart/pubspec.yaml
  58. 5 11
      dart/server.dart
  59. 6 6
      dart/setup.py
  60. 3 3
      django/setup.py
  61. 3 3
      django/setup_pg.py
  62. 3 3
      django/setup_py3.py
  63. 4 4
      dropwizard/setup.py
  64. 6 6
      elli/setup_erlang.py
  65. 8 0
      evhttp-sharp/.gitignore
  66. 24 0
      evhttp-sharp/benchmark_config
  67. 28 0
      evhttp-sharp/setup.py
  68. 69 0
      evhttp-sharp/src/EvHttpSharpBenchmark.csproj
  69. 20 0
      evhttp-sharp/src/EvHttpSharpBenchmark.sln
  70. 37 0
      evhttp-sharp/src/Program.cs
  71. 36 0
      evhttp-sharp/src/Properties/AssemblyInfo.cs
  72. BIN
      evhttp-sharp/src/lib/EvHttpSharp.dll
  73. BIN
      evhttp-sharp/src/lib/Newtonsoft.Json.dll
  74. BIN
      evhttp-sharp/src/lib/libevent_core-2-0-5.dll
  75. BIN
      evhttp-sharp/src/lib/libevent_extra-2-0-5.dll
  76. BIN
      evhttp-sharp/src/libevent_core-2-0-5.dll
  77. BIN
      evhttp-sharp/src/libevent_extra-2-0-5.dll
  78. 12 12
      express/setup.py
  79. 3 3
      falcon/setup.py
  80. 3 3
      falcon/setup_py3.py
  81. 3 3
      falcon/setup_pypy.py
  82. 9 9
      falcore/setup.py
  83. 1 3
      falcore/src/framework_benchmarks/falcore.go
  84. 7 7
      finagle/setup.py
  85. 65 35
      finagle/src/main/scala/com/falmarri/finagle/Finagle.scala
  86. 3 3
      flask/setup.py
  87. 6 6
      flask/setup_nginxuwsgi.py
  88. 3 3
      flask/setup_py3.py
  89. 3 3
      flask/setup_pypy.py
  90. 7 6
      gemini/setup.py
  91. 8 8
      go/setup.py
  92. 1 3
      go/src/hello/hello.go
  93. 21 4
      grails/README.md
  94. 5 3
      grails/benchmark_config
  95. 15 13
      grails/hello/.classpath
  96. 17 0
      grails/hello/.gitignore
  97. 7 1
      grails/hello/.project
  98. 2 2
      grails/hello/application.properties
  99. 32 5
      grails/hello/grails-app/conf/BuildConfig.groovy
  100. 49 42
      grails/hello/grails-app/conf/Config.groovy

+ 54 - 4
.gitignore

@@ -1,10 +1,14 @@
 .DS_Store
 .DS_Store
-*.pyc
-installs/
+installs
 *.log
 *.log
-*.lock
 node_modules/
 node_modules/
-*.war
+
+# eclipse
+.classpath
+.project
+.settings
+
+# maven
 target/
 target/
 *.out
 *.out
 *.class
 *.class
@@ -12,9 +16,55 @@ mods/
 /.settings
 /.settings
 /.buildpath
 /.buildpath
 /.project
 /.project
+*/src/main/java/META-INF/
+*.versionsBackup
+bin/
+
+# common junk
+*.log
+*.swp
+*.diff
+*.patch
+*/bin/
+
+# intellij
 *.iml
 *.iml
+*.ipr
+*.iws
 .idea/
 .idea/
 .hsenv/
 .hsenv/
 azure.err
 azure.err
 php-kohana/application/logs/
 php-kohana/application/logs/
 php-fuel/fuel/app/logs/
 php-fuel/fuel/app/logs/
+results/
+benchmark.cfg
+
+# Mac filesystem dust
+.DS_Store
+
+# pmd
+.pmdruleset
+.pmd
+
+# netbeans
+/nbproject
+
+# vim
+.*.sw[a-p]
+
+# merge tooling
+*.orig
+
+# maven
+*.versionsBackup
+*.releaseBackup
+
+# python-isms
+*.pyc
+
+# java / ruby stuff
+*.lock
+*.war
+
+# go
+go/pkg/

+ 4 - 4
HttpListener/setup.py

@@ -3,20 +3,20 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   if os.name != 'nt':
   if os.name != 'nt':
     return 1
     return 1
   
   
   try:
   try:
     setup_util.replace_text("HttpListener/HttpListener/App.config", "localhost", args.database_host)
     setup_util.replace_text("HttpListener/HttpListener/App.config", "localhost", args.database_host)
-    subprocess.check_call("powershell -Command .\\setup.ps1 start", cwd="HttpListener")
+    subprocess.check_call("powershell -Command .\\setup.ps1 start", cwd="HttpListener", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name != 'nt':
   if os.name != 'nt':
     return 0
     return 0
   
   
-  subprocess.check_call("powershell -Command .\\setup.ps1 stop", cwd="HttpListener")
+  subprocess.check_call("powershell -Command .\\setup.ps1 stop", cwd="HttpListener", stderr=errfile, stdout=logfile)
   return 0
   return 0

+ 9 - 9
README.md

@@ -164,12 +164,12 @@ Here is an example of Wicket's setup file.
 	import setup_util
 	import setup_util
 
 
 	##################################################
 	##################################################
-	# start(args)
+	# start(args, logfile, errfile)
 	#
 	#
 	# Starts the server for Wicket
 	# Starts the server for Wicket
 	# returns 0 if everything completes, 1 otherwise
 	# returns 0 if everything completes, 1 otherwise
 	##################################################
 	##################################################
-	def start(args):
+	def start(args, logfile, errfile):
 
 
     # setting the database url
     # setting the database url
     setup_util.replace_text("wicket/src/main/webapp/WEB-INF/resin-web.xml", "mysql:\/\/.*:3306", "mysql://" + args.database_host + ":3306")
     setup_util.replace_text("wicket/src/main/webapp/WEB-INF/resin-web.xml", "mysql:\/\/.*:3306", "mysql://" + args.database_host + ":3306")
@@ -179,23 +179,23 @@ Here is an example of Wicket's setup file.
     # 3. Copy package to Resin's webapp directory
     # 3. Copy package to Resin's webapp directory
     # 4. Start resin
     # 4. Start resin
     try:
     try:
-      subprocess.check_call("mvn clean compile war:war", shell=True, cwd="wicket")
-      subprocess.check_call("rm -rf $RESIN_HOME/webapps/*", shell=True)
-      subprocess.check_call("cp wicket/target/hellowicket-1.0-SNAPSHOT.war $RESIN_HOME/webapps/wicket.war", shell=True)
-      subprocess.check_call("$RESIN_HOME/bin/resinctl start", shell=True)
+      subprocess.check_call("mvn clean compile war:war", shell=True, cwd="wicket", stderr=errfile, stdout=logfile)
+      subprocess.check_call("rm -rf $RESIN_HOME/webapps/*", shell=True, stderr=errfile, stdout=logfile)
+      subprocess.check_call("cp wicket/target/hellowicket-1.0-SNAPSHOT.war $RESIN_HOME/webapps/wicket.war", shell=True, stderr=errfile, stdout=logfile)
+      subprocess.check_call("$RESIN_HOME/bin/resinctl start", shell=True, stderr=errfile, stdout=logfile)
       return 0
       return 0
     except subprocess.CalledProcessError:
     except subprocess.CalledProcessError:
       return 1
       return 1
 
 
 	##################################################
 	##################################################
-	# stop()
+	# stop(logfile, errfile)
 	#
 	#
 	# Stops the server for Wicket
 	# Stops the server for Wicket
 	# returns 0 if everything completes, 1 otherwise
 	# returns 0 if everything completes, 1 otherwise
 	##################################################
 	##################################################
-	def stop():
+	def stop(logfile):
     try:
     try:
-      subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True)
+      subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True, stderr=errfile, stdout=logfile)
       return 0
       return 0
     except subprocess.CalledProcessError:
     except subprocess.CalledProcessError:
       return 1
       return 1

+ 14 - 0
UrWeb/README.md

@@ -0,0 +1,14 @@
+TechEmpower Framework Benchmark: Ur/Web
+=======================================
+
+http://www.impredicative.com/ur/
+
+To compile a standalone executable running on port 8080, run `urweb bench`.
+
+`bench.ur` is the main source file. `bench.urs` is the signature file which describes the module's exported functions. `bench.urp` is the project file which gives compilation directives.
+
+`fortune.sql` and `world.sql` are for the creation and population of the `uw_Bench_fortune` and `uw_Bench_world` tables. As part of its strong security guarantees, Ur/Web follows a certain naming convention for SQL tables which is incompatible with the default table names of `fortune` and `world`.
+
+`benchmark_config` and `source_code` include metadata for the framework comparison.
+
+`__init__.py` and `setup.py` are for starting and stopping the Ur/Web server.

+ 0 - 0
UrWeb/__init__.py


BIN
UrWeb/bench.exe


+ 81 - 0
UrWeb/bench.ur

@@ -0,0 +1,81 @@
+open Json
+
+fun addHeaders () =
+  n <- now;
+  setHeader (blessResponseHeader "Date") (timef "%a, %d %b %Y %H:%M:%S GMT" n);
+  setHeader (blessResponseHeader "Server") "Ur/Web"
+fun clamp n =
+  (n % 10000) + 1
+fun parseQueries oqs : int =
+  let
+    val qt = case oqs of
+        None => Some ("queries", "1")
+      | Some qs => String.split (show qs) #"="
+    val on = case qt of
+        Some ("queries", x) => read x
+      | _ => Some 1
+  in
+    case on of
+      None => 1
+    | Some x => if x > 500 then 500
+                else if x < 1 then 1
+                else x
+  end
+fun returnJson [a] (_ : json a) (j : a) : transaction page =
+  addHeaders ();
+  returnBlob (textBlob (toJson j)) (blessMime "application/json")
+fun returnText (t : string) : transaction page =
+  addHeaders ();
+  returnBlob (textBlob t) (blessMime "text/plain")
+
+val hello = "Hello, World!"
+fun plaintext () =
+  returnText hello
+
+type json_t = {Message : string}
+val json_conversion : json json_t = json_record {Message = "message"}
+val hello_json = {Message = hello}
+fun json () =
+    returnJson hello_json
+
+table world : {Id : int, RandomNumber : int} PRIMARY KEY Id
+type world_t = {Id : int, RandomNumber : int}
+val world_conversion : json world_t = json_record {Id = "id", RandomNumber = "randomNumber"}
+fun world_find n =
+  oneRow1 (SELECT World.Id, World.RandomNumber FROM world WHERE World.Id = {[n]})
+
+fun db () =
+  n <- rand;
+  row <- world_find (clamp n);
+  returnJson row
+
+fun queries oqs =
+  rows <- List.tabulateM (fn _ => n <- rand; world_find (clamp n)) (parseQueries oqs);
+  returnJson rows
+
+fun updates oqs =
+  rows <- List.tabulateM (fn _ => n <- rand; world_find (clamp n)) (parseQueries oqs);
+  rows' <- List.mapM (fn r => n <- rand; return (r -- #RandomNumber ++ {RandomNumber = clamp n})) rows;
+  u <- List.mapM (fn r => dml (UPDATE world SET RandomNumber = {[r.RandomNumber]} WHERE Id = {[r.Id]})) rows';
+  returnJson rows'
+
+table fortune : {Id : int, Message : string} PRIMARY KEY Id
+type fortune_t = {Id : int, Message : string}
+val fortune_conversion : json fortune_t = json_record {Id = "id", Message = "message"}
+val new_fortune : fortune_t = {Id = 0, Message = "Additional fortune added at request time"}
+fun fortunes () =
+  fs <- queryL1 (SELECT Fortune.Id, Fortune.Message FROM fortune);
+  let
+    val fs' = List.sort (fn x y => x.Message > y.Message ) (new_fortune :: fs)
+  in
+    addHeaders ();
+    return <xml>
+             <head><title>Fortunes</title></head>
+             <body><table>
+               <tr><th>id</th><th>message</th></tr>
+               {List.mapX (fn f => <xml><tr>
+                 <td>{[f.Id]}</td><td>{[f.Message]}</td>
+               </tr></xml>) fs'}
+             </table></body>
+           </xml>
+  end

+ 13 - 0
UrWeb/bench.urp

@@ -0,0 +1,13 @@
+library meta
+database dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass host=localhost
+minHeap 16384
+rewrite url Bench/*
+allow responseHeader Date
+allow responseHeader Server
+allow mime application/json
+allow mime text/plain
+safeGet updates
+
+$/list
+$/string
+bench

+ 6 - 0
UrWeb/bench.urs

@@ -0,0 +1,6 @@
+val json : unit -> transaction page
+val plaintext : unit -> transaction page
+val db : unit -> transaction page
+val queries: option queryString -> transaction page
+val fortunes: unit -> transaction page
+val updates: option queryString -> transaction page

+ 26 - 0
UrWeb/benchmark_config

@@ -0,0 +1,26 @@
+{
+  "framework": "urweb",
+  "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": "Fullstack",
+      "database": "Postgres",
+      "framework": "ur/web",
+      "language": "Ur",
+      "orm": "Micro",
+      "platform": "Ur/Web",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ur/web"
+    }
+  }]
+}

+ 28 - 0
UrWeb/create-postgres.sql

@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS uw_Bench_world;
+
+CREATE TABLE uw_Bench_world AS
+SELECT uw_id::int8, trunc(random()*9999+1)::int8 AS uw_randomnumber
+FROM generate_series(1,10000) AS uw_id;
+
+ALTER TABLE uw_Bench_world ADD PRIMARY KEY (uw_id);
+ALTER TABLE uw_Bench_world ALTER COLUMN uw_randomnumber SET NOT NULL;
+
+DROP TABLE IF EXISTS uw_Bench_fortune;
+CREATE TABLE uw_Bench_fortune (
+  uw_id int8 NOT NULL,
+  uw_message text NOT NULL,
+  PRIMARY KEY (uw_id)
+);
+
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.")</script>');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (7, 'Any program that runs right is obsolete.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (9, 'Feature: A bug with seniority.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (1, 'fortune: No such file or directory');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (12, 'フレームワークのベンチマーク');

+ 14 - 0
UrWeb/fortune.sql

@@ -0,0 +1,14 @@
+CREATE TABLE uw_Bench_fortune(uw_id int8 NOT NULL, uw_message text NOT NULL,
+  PRIMARY KEY (uw_id));
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.")</script>');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (7, 'Any program that runs right is obsolete.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (9, 'Feature: A bug with seniority.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (1, 'fortune: No such file or directory');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (12, 'フレームワークのベンチマーク');

+ 25 - 0
UrWeb/meta/LICENSE

@@ -0,0 +1,25 @@
+Copyright (c) 2009-2010, Adam Chlipala
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+- The names of contributors may not be used to endorse or promote products
+  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 76 - 0
UrWeb/meta/eq.ur

@@ -0,0 +1,76 @@
+con eq = K ==> fn (t1 :: K) (t2 :: K) => f :: (K -> Type) -> f t1 -> f t2
+
+val refl [K] [t ::: K] : eq t t = fn [f :: (K -> Type)] x => x
+
+fun sym [K] [t1 ::: K] [t2 ::: K] (e : eq t1 t2) : eq t2 t1 =
+    e [fn t => eq t t1] refl
+
+fun trans [K] [t1 ::: K] [t2 ::: K] [t3 ::: K] (e1 : eq t1 t2) (e2 : eq t2 t3) : eq t1 t3 =
+    (sym e1) [fn t => eq t t3] e2
+
+fun cast [K] [t1 ::: K] [t2 ::: K] (e : eq t1 t2) = e
+
+fun fold [K] [tf :: {K} -> Type] [r ::: {K}]
+         (f : pre :: {K} -> nm :: Name -> v :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+          eq r (pre ++ [nm = v] ++ post) -> tf post -> tf ([nm = v] ++ post))
+    (i : tf []) (fl : folder r) : tf r =
+    @@Top.fold [fn post => pre :: {K} -> [pre ~ post] => eq r (pre ++ post) -> tf post]
+     (fn [nm :: Name] [t :: K] [rest :: {K}] [[nm] ~ rest]
+                      (acc : pre :: {K} -> [pre ~ rest] => eq r (pre ++ rest) -> tf rest)
+                      [pre :: {K}] [pre ~ [nm = t] ++ rest] pf =>
+         f [pre] [nm] [t] [rest] pf (acc [[nm = t] ++ pre] pf))
+     (fn [pre :: {K}] [pre ~ []] _ => i) [r] fl [[]] ! refl
+
+fun foldUR [tr :: Type] [tf :: {Unit} -> Type] [r ::: {Unit}]
+    (f : pre :: {Unit} -> nm :: Name -> post :: {Unit} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+     eq r (pre ++ [nm] ++ post) -> tr -> tf post -> tf ([nm] ++ post))
+    (i : tf []) (fl : folder r) (r : $(mapU tr r)) : tf r =
+    @@fold [fn r' => $(mapU tr r') -> tf r'] [r]
+      (fn [pre :: {Unit}] [nm :: Name] [u :: Unit] [post :: {Unit}] [pre ~ post] [[nm] ~ pre ++ post] pf acc r =>
+          f [pre] [nm] [post] pf r.nm (acc (r -- nm)))
+      (fn _ => i) fl r
+
+fun foldR [K] [tr :: K -> Type] [tf :: {K} -> Type] [r ::: {K}]
+    (f : pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+     eq r (pre ++ [nm = t] ++ post) -> tr t -> tf post -> tf ([nm = t] ++ post))
+    (i : tf []) (fl : folder r) (r : $(map tr r)) : tf r =
+    @@fold [fn r' => $(map tr r') -> tf r'] [r]
+      (fn [pre :: {K}] [nm :: Name] [t :: K] [post :: {K}] [pre ~ post] [[nm] ~ pre ++ post] pf acc r =>
+          f [pre] [nm] [t] [post] pf r.nm (acc (r -- nm)))
+      (fn _ => i) fl r
+
+fun foldR2 [K] [tr1 :: K -> Type] [tr2 :: K -> Type] [tf :: {K} -> Type] [r ::: {K}]
+    (f : pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+     eq r (pre ++ [nm = t] ++ post) -> tr1 t -> tr2 t -> tf post -> tf ([nm = t] ++ post))
+    (i : tf []) (fl : folder r) (r1 : $(map tr1 r)) (r2 : $(map tr2 r)) : tf r =
+    @@fold [fn r' => $(map tr1 r') -> $(map tr2 r') -> tf r'] [r]
+      (fn [pre :: {K}] [nm :: Name] [t :: K] [post :: {K}] [pre ~ post] [[nm] ~ pre ++ post] pf acc r1 r2 =>
+          f [pre] [nm] [t] [post] pf r1.nm r2.nm (acc (r1 -- nm) (r2 -- nm)))
+      (fn _ _ => i) fl r1 r2
+
+fun foldR3 [K] [tr1 :: K -> Type] [tr2 :: K -> Type] [tr3 :: K -> Type] [tf :: {K} -> Type] [r ::: {K}]
+    (f : pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+     eq r (pre ++ [nm = t] ++ post) -> tr1 t -> tr2 t -> tr3 t -> tf post -> tf ([nm = t] ++ post))
+    (i : tf []) (fl : folder r) (r1 : $(map tr1 r)) (r2 : $(map tr2 r)) (r3 : $(map tr3 r)) : tf r =
+    @@fold [fn r' => $(map tr1 r') -> $(map tr2 r') -> $(map tr3 r') -> tf r'] [r]
+      (fn [pre :: {K}] [nm :: Name] [t :: K] [post :: {K}] [pre ~ post] [[nm] ~ pre ++ post] pf acc r1 r2 r3 =>
+          f [pre] [nm] [t] [post] pf r1.nm r2.nm r3.nm (acc (r1 -- nm) (r2 -- nm) (r3 -- nm)))
+      (fn _ _ _ => i) fl r1 r2 r3
+
+fun foldR4 [K] [tr1 :: K -> Type] [tr2 :: K -> Type] [tr3 :: K -> Type] [tr4 :: K -> Type] [tf :: {K} -> Type] [r ::: {K}]
+    (f : pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+     eq r (pre ++ [nm = t] ++ post) -> tr1 t -> tr2 t -> tr3 t -> tr4 t -> tf post -> tf ([nm = t] ++ post))
+    (i : tf []) (fl : folder r) (r1 : $(map tr1 r)) (r2 : $(map tr2 r)) (r3 : $(map tr3 r)) (r4 : $(map tr4 r)) : tf r =
+    @@fold [fn r' => $(map tr1 r') -> $(map tr2 r') -> $(map tr3 r') -> $(map tr4 r') -> tf r'] [r]
+      (fn [pre :: {K}] [nm :: Name] [t :: K] [post :: {K}] [pre ~ post] [[nm] ~ pre ++ post] pf acc r1 r2 r3 r4 =>
+          f [pre] [nm] [t] [post] pf r1.nm r2.nm r3.nm r4.nm (acc (r1 -- nm) (r2 -- nm) (r3 -- nm) (r4 -- nm)))
+      (fn _ _ _ _ => i) fl r1 r2 r3 r4
+
+fun mp [K] [tr :: K -> Type] [tf :: K -> Type] [r ::: {K}]
+       (f : nm :: Name -> t :: K -> rest :: {K} -> [[nm] ~ rest] =>
+        eq r ([nm = t] ++ rest) -> tr t -> tf t)
+    (fl : folder r) (r : $(map tr r)) : $(map tf r) =
+  @@foldR [tr] [fn r => $(map tf r)] [r]
+      (fn [pre :: {K}] [nm :: Name] [t :: K] [post :: {K}] [pre ~ post] [[nm] ~ pre ++ post] pf r acc =>
+          {nm = f [nm] [t] [pre ++ post] pf r} ++ acc)
+      {} fl r

+ 44 - 0
UrWeb/meta/eq.urs

@@ -0,0 +1,44 @@
+(** A constructor equality predicate *)
+
+con eq :: K --> K -> K -> Type
+
+val refl : K --> t ::: K -> eq t t
+val sym : K --> t1 ::: K -> t2 ::: K -> eq t1 t2 -> eq t2 t1
+val trans : K --> t1 ::: K -> t2 ::: K -> t3 ::: K -> eq t1 t2 -> eq t2 t3 -> eq t1 t3
+
+val cast : K --> t1 ::: K -> t2 ::: K -> eq t1 t2 -> f :: (K -> Type) -> f t1 -> f t2
+
+val fold : K --> tf :: ({K} -> Type) -> r ::: {K}
+           -> (pre :: {K} -> nm :: Name -> v :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+               eq r (pre ++ [nm = v] ++ post) -> tf post -> tf ([nm = v] ++ post))
+           -> tf [] -> folder r -> tf r
+
+val foldUR : tr :: Type -> tf :: ({Unit} -> Type) -> r ::: {Unit}
+           -> (pre :: {Unit} -> nm :: Name -> post :: {Unit} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+               eq r (pre ++ [nm] ++ post) -> tr -> tf post -> tf ([nm] ++ post))
+           -> tf [] -> folder r -> $(mapU tr r) -> tf r
+
+val foldR : K --> tr :: (K -> Type) -> tf :: ({K} -> Type) -> r ::: {K}
+           -> (pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+               eq r (pre ++ [nm = t] ++ post) -> tr t -> tf post -> tf ([nm = t] ++ post))
+           -> tf [] -> folder r -> $(map tr r) -> tf r
+
+val foldR2 : K --> tr1 :: (K -> Type) -> tr2 :: (K -> Type) -> tf :: ({K} -> Type) -> r ::: {K}
+             -> (pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+                 eq r (pre ++ [nm = t] ++ post) -> tr1 t -> tr2 t -> tf post -> tf ([nm = t] ++ post))
+             -> tf [] -> folder r -> $(map tr1 r) -> $(map tr2 r) -> tf r
+
+val foldR3 : K --> tr1 :: (K -> Type) -> tr2 :: (K -> Type) -> tr3 :: (K -> Type) -> tf :: ({K} -> Type) -> r ::: {K}
+             -> (pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+                 eq r (pre ++ [nm = t] ++ post) -> tr1 t -> tr2 t -> tr3 t -> tf post -> tf ([nm = t] ++ post))
+             -> tf [] -> folder r -> $(map tr1 r) -> $(map tr2 r) -> $(map tr3 r) -> tf r
+
+val foldR4 : K --> tr1 :: (K -> Type) -> tr2 :: (K -> Type) -> tr3 :: (K -> Type) -> tr4 :: (K -> Type) -> tf :: ({K} -> Type) -> r ::: {K}
+             -> (pre :: {K} -> nm :: Name -> t :: K -> post :: {K} -> [pre ~ post] => [[nm] ~ pre ++ post] =>
+                 eq r (pre ++ [nm = t] ++ post) -> tr1 t -> tr2 t -> tr3 t -> tr4 t -> tf post -> tf ([nm = t] ++ post))
+             -> tf [] -> folder r -> $(map tr1 r) -> $(map tr2 r) -> $(map tr3 r) -> $(map tr4 r) -> tf r
+
+val mp : K --> tr :: (K -> Type) -> tf :: (K -> Type) -> r ::: {K}
+         -> (nm :: Name -> t :: K -> rest :: {K} -> [[nm] ~ rest] =>
+             eq r ([nm = t] ++ rest) -> tr t -> tf t)
+         -> folder r -> $(map tr r) -> $(map tf r)

+ 144 - 0
UrWeb/meta/html.ur

@@ -0,0 +1,144 @@
+open Parse
+
+con attribute = fn t => {Nam : string,
+                         Parse : string -> option t}
+
+con tag = fn ts => {Nam : string,
+                    Attributes : $(map attribute ts),
+                    Folder : folder ts,
+                    Construct : ctx ::: {Unit} -> [[Body] ~ ctx] => $ts
+                                -> xml ([Body] ++ ctx) [] [] -> xml ([Body] ++ ctx) [] []}
+
+fun tag [use] [ignore] [use ~ ignore] (fl : folder use) (name : string) (attrs : $(map attribute use))
+        (construct : ctx ::: {Unit} -> [[Body] ~ ctx] => Basis.tag (use ++ ignore) ([Body] ++ ctx) ([Body] ++ ctx) [] []) =
+    {Nam = name,
+     Attributes = attrs,
+     Folder = fl,
+     Construct = fn [ctx] [[Body] ~ ctx] (ats : $use) (inner : xml ([Body] ++ ctx) [] []) =>
+                    Basis.tag null None noStyle None ats construct inner}
+
+fun simpleTag [ignore] name (bt : bodyTag ignore) : tag [] =
+    @@tag [[]] [ignore] ! _ name {} (fn [ctx] [[Body] ~ ctx] => bt ())
+
+fun simpleTag' [use] [ignore] [use ~ ignore] (fl : folder use)
+               name (bt : bodyTag (use ++ ignore)) (ats : $(map attribute use)) : tag use =
+    @@tag [use] [ignore] ! fl name ats (fn [ctx] [[Body] ~ ctx] => bt ())
+
+fun url name = {Nam = name,
+                Parse = checkUrl}
+
+datatype error a =
+         Good of a
+       | Bad of string
+
+fun format [tags] (fl : folder tags) (tags : $(map tag tags)) [ctx] [[Body] ~ ctx] s =
+    let
+        fun loop s : error (xml ([Body] ++ ctx) [] [] * string) =
+            case String.msplit {Haystack = s, Needle = "&<"} of
+                None => Good (cdata s, "")
+              | Some (pre, ch, post) =>
+                case ch of
+                    #"&" =>
+                    (case String.split post #";" of
+                         None => Bad "No ';' after '&'"
+                       | Some (code, post) =>
+                         let
+                             val xml = 
+                                 case code of
+                                     "lt" => <xml>&lt;</xml>
+                                   | "gt" => <xml>&gt;</xml>
+                                   | "amp" => <xml>&amp;</xml>
+                                   | _ => <xml/>
+                         in
+                             case loop post of
+                                 Good (after, post) => Good (<xml>{[pre]}{xml}{after}</xml>, post)
+                               | x => x
+                         end)
+                  | _ =>
+                    if String.length post > 0 && String.sub post 0 = #"/" then
+                        case String.split post #"\x3E" of
+                            None => Bad "No '>' after '</'"
+                          | Some (_, post) => Good (<xml>{[pre]}</xml>, post)
+                    else
+                        case String.msplit {Haystack = post, Needle = " >"} of
+                            None => Bad "No '>' after '<'"
+                          | Some (tname, ch, post) =>
+                            @foldR [tag] [fn _ => unit -> error (xml ([Body] ++ ctx) [] [] * string)]
+                            (fn [nm :: Name] [ts :: {Type}] [r :: {{Type}}] [[nm] ~ r] (meta : tag ts) acc () =>
+                                if meta.Nam = tname then
+                                    let
+                                        fun doAttrs (ch, post, ats : $(map option ts)) =
+                                            if String.length post > 0 && Char.isSpace (String.sub post 0) then
+                                                doAttrs (ch, String.substring post {Start = 1,
+                                                                                    Len = String.length post - 1},
+                                                         ats)
+                                            else 
+                                                case ch of
+                                                    #"\x3E" => Good (ats, post)
+                                                  | _ =>
+                                                    case String.split post #"=" of
+                                                        None =>
+                                                        (case String.split post #"\x3E" of
+                                                             None => Bad "No tag ender '\x3E'"
+                                                           | Some (_, post) => Good (ats, post))
+                                                      | Some (aname, post) =>
+                                                        if String.length post >= 1 && String.sub post 0 = #"\"" then
+                                                            case String.split (String.substring post
+                                                                                                {Start = 1,
+                                                                                                 Len = String.length post
+                                                                                                       - 1})
+                                                                              #"\"" of
+                                                                None => Bad "No '\"' to end attribute value"
+                                                              | Some (aval, post) =>
+                                                                let
+                                                                    val ats =
+                                                                        @map2 [attribute] [option] [option]
+                                                                         (fn [t] meta v =>
+                                                                             if aname = meta.Nam then
+                                                                                 meta.Parse aval
+                                                                             else
+                                                                                 v)
+                                                                         meta.Folder meta.Attributes ats
+                                                                in
+                                                                    doAttrs (#" ", post, ats)
+                                                                end
+                                                        else
+                                                            Bad "Attribute value doesn't begin with quote"
+                                    in
+                                        case doAttrs (ch, post, @map0 [option] (fn [t :: Type] => None)
+                                                                 meta.Folder) of
+                                            Good (ats, post) =>
+                                            let
+                                                val ats =
+                                                    @map2 [attribute] [option] [ident]
+                                                     (fn [t] meta v =>
+                                                         case v of
+                                                             None => error <xml>Missing attribute {[meta.Nam]}
+                                                               for {[tname]}</xml>
+                                                           | Some v => v)
+                                                     meta.Folder meta.Attributes ats
+                                            in
+                                                case loop post of
+                                                    Good (inner, post) =>
+                                                    (case loop post of
+                                                         Good (after, post) =>
+                                                         Good (<xml>{[pre]}{meta.Construct [ctx] !
+                                                                                           ats inner}{after}</xml>, post)
+                                                       | x => x)
+                                                  | x => x
+                                            end
+                                          | Bad s => Bad s
+                                    end
+                                else
+                                    acc ())
+                            (fn () => Bad ("Unknown HTML tag " ^ tname)) fl tags ()
+    in
+        case loop s of
+            Bad msg => Failure msg
+          | Good (xml, _) => Success xml
+    end
+
+val b = simpleTag "b" @@b
+val i = simpleTag "i" @@i
+val a = simpleTag' "a" @@a {Href = url "href"}
+

+ 29 - 0
UrWeb/meta/html.urs

@@ -0,0 +1,29 @@
+(** Safe HTML parsing *)
+
+con attribute = fn t => {Nam : string,
+                         Parse : string -> option t}
+
+con tag = fn ts => {Nam : string,
+                    Attributes : $(map attribute ts),
+                    Folder : folder ts,
+                    Construct : ctx ::: {Unit} -> [[Body] ~ ctx] => $ts
+                                -> xml ([Body] ++ ctx) [] [] -> xml ([Body] ++ ctx) [] []}
+
+val tag : use ::: {Type} -> ignore ::: {Type} -> [use ~ ignore] => folder use -> string
+          -> $(map attribute use)
+          -> (ctx ::: {Unit} -> [[Body] ~ ctx] => Basis.tag (use ++ ignore) ([Body] ++ ctx) ([Body] ++ ctx) [] [])
+          -> tag use
+
+val simpleTag : ignore ::: {Type} -> string -> bodyTag ignore -> tag []
+val simpleTag' : use ::: {Type} -> ignore ::: {Type} -> [use ~ ignore] => folder use
+    -> string -> bodyTag (use ++ ignore) -> $(map attribute use) -> tag use
+
+val url : string -> attribute url
+
+val format : tags ::: {{Type}} -> folder tags -> $(map tag tags)
+             -> ctx ::: {Unit} -> [[Body] ~ ctx] => string
+             -> Parse.parse (xml ([Body] ++ ctx) [] [])
+
+val b : tag []
+val i : tag []
+val a : tag [Href = url]

+ 40 - 0
UrWeb/meta/incl.ur

@@ -0,0 +1,40 @@
+con incl' = K ==> fn (r1 :: {K}) (r2 :: {K}) (r' :: {K}) =>
+                     [r1 ~ r'] => {Expose : f :: ({K} -> Type) -> f r2 -> f (r1 ++ r'),
+                                   Hide : f :: ({K} -> Type) -> f (r1 ++ r') -> f r2}
+
+con incl = K ==> fn (r1 :: {K}) (r2 :: {K}) =>
+                    tp :: Type -> (r' :: {K} -> [r1 ~ r'] => incl' r1 r2 r' -> tp) -> tp
+
+fun incl [K] [r1 :: {K}] [r2 :: {K}] [r1 ~ r2] =
+ fn [tp :: Type] (f : r' :: {K} -> [r1 ~ r'] => incl' r1 (r1 ++ r2) r' -> tp) =>
+    f [r2] (fn [r1 ~ r2] => {Expose = fn [f :: ({K} -> Type)] x => x,
+                             Hide = fn [f :: ({K} -> Type)] x => x})
+    
+fun proj [r1 ::: {Type}] [r2 ::: {Type}] (i : incl r1 r2) (r : $r2) =
+    i [$r1] (fn [r' :: {Type}] [r1 ~ r'] (i' : incl' r1 r2 r') =>
+                i'.Expose [fn r => $r] r --- r')
+
+fun inv1 [K] [nm :: Name] [t :: K] [r :: {K}] [r' :: {K}] [[nm] ~ r]
+         [f :: Name -> K -> {K} -> Type]
+         (i : incl ([nm = t] ++ r) r')
+         (f : nm :: Name -> t :: K -> r :: {K} -> [[nm] ~ r] => f nm t ([nm = t] ++ r)) =
+    i [f nm t r'] (fn [r'' :: {K}] [[nm = t] ++ r ~ r''] (i' : incl' ([nm = t] ++ r) r' r'') =>
+                      i'.Hide [f nm t] (f [nm] [t] [r ++ r'']))
+
+fun inv2 [K] [nm :: Name] [t :: K] [r :: {K}] [r' :: {K}] [[nm] ~ r]
+         (i : incl ([nm = t] ++ r) r') =
+    i [incl r r'] (fn [r'' :: {K}] [[nm = t] ++ r ~ r''] (i' : incl' ([nm = t] ++ r) r' r'') =>
+                   fn [tp :: Type] (f : r''' :: {K} -> [r ~ r'''] => incl' r r' r''' -> tp) =>
+                      f [[nm = t] ++ r''] (fn [r ~ [nm = t] ++ r''] =>
+                                              {Expose = fn [f :: ({K} -> Type)] (x : f r') => i'.Expose [f] x,
+                                               Hide = fn [f :: ({K} -> Type)] x => i'.Hide [f] x}))
+
+fun fold [K] [tf :: {K} -> Type] [r ::: {K}]
+         (f : nm :: Name -> v :: K -> r' :: {K}
+              -> [[nm] ~ r'] => incl ([nm = v] ++ r') r -> tf r' -> tf ([nm = v] ++ r'))
+         (i : tf []) (fl : folder r) =
+    @Top.fold [fn r' => incl r' r -> tf r']
+     (fn [nm :: Name] [v :: K] [r' :: {K}] [[nm] ~ r'] acc i =>
+         f [nm] [v] [r'] i (acc (inv2 [nm] [v] [r'] [r] i)))
+     (fn _ => i)
+     fl (incl [r] [[]])

+ 22 - 0
UrWeb/meta/incl.urs

@@ -0,0 +1,22 @@
+(** A record inclusion predicate *)
+
+con incl :: K --> {K} -> {K} -> Type
+
+val incl : K --> r1 :: {K} -> r2 :: {K} -> [r1 ~ r2] => incl r1 (r1 ++ r2)
+val proj : r1 ::: {Type} -> r2 ::: {Type} -> incl r1 r2 -> $r2 -> $r1
+
+val inv1 : K --> nm :: Name -> t :: K -> r :: {K} -> r' :: {K}
+           -> [[nm] ~ r] =>
+    f :: (Name -> K -> {K} -> Type)
+    -> incl ([nm = t] ++ r) r'
+    -> (nm :: Name -> t :: K -> r :: {K} -> [[nm] ~ r] => f nm t ([nm = t] ++ r))
+    -> f nm t r'
+val inv2 : K --> nm :: Name -> t :: K -> r :: {K} -> r' :: {K}
+           -> [[nm] ~ r] =>
+    incl ([nm = t] ++ r) r' -> incl r r'
+
+val fold : K --> tf :: ({K} -> Type) -> r ::: {K}
+           -> (nm :: Name -> v :: K -> r' :: {K}
+               -> [[nm] ~ r'] => incl ([nm = v] ++ r') r -> tf r' -> tf ([nm = v] ++ r'))
+           -> tf []
+           -> folder r -> tf r

+ 339 - 0
UrWeb/meta/json.ur

@@ -0,0 +1,339 @@
+con json a = {ToJson : a -> string,
+              FromJson : string -> a * string}
+
+fun mkJson [a] (x : {ToJson : a -> string,
+                     FromJson : string -> a * string}) = x
+
+fun skipSpaces s =
+    let
+        val len = String.length s
+
+        fun skip i =
+            if i >= len then
+                ""
+            else
+                let
+                    val ch = String.sub s i
+                in
+                    if Char.isSpace ch then
+                        skip (i+1)
+                    else
+                        String.substring s {Start = i, Len = len-i}
+                end
+    in
+        skip 0
+    end
+
+fun toJson [a] (j : json a) : a -> string = j.ToJson
+fun fromJson' [a] (j : json a) : string -> a * string = j.FromJson
+
+fun fromJson [a] (j : json a) (s : string) : a =
+    let
+        val (v, s') = j.FromJson (skipSpaces s)
+    in
+        if String.all Char.isSpace s' then
+            v
+        else
+            error <xml>Extra content at end of JSON record: {[s']}</xml>
+    end
+
+fun escape s =
+    let
+        val len = String.length s
+
+        fun esc i =
+            if i >= len then
+                "\""
+            else
+                let
+                    val ch = String.sub s i
+                in
+                    (if ch = #"\"" || ch = #"\\" then
+                         "\\" ^ String.str ch
+                     else
+                         String.str ch) ^ esc (i+1)
+                end
+    in
+        "\"" ^ esc 0
+    end
+
+fun unescape s =
+    let
+        val len = String.length s
+
+        fun findEnd i =
+            if i >= len then
+                error <xml>JSON unescape: string ends before quote: {[s]}</xml>
+            else
+                let
+                    val ch = String.sub s i
+                in
+                    case ch of
+                        #"\"" => i
+                      | #"\\" =>
+                        if i+1 >= len then
+                            error <xml>JSON unescape: Bad escape sequence: {[s]}</xml>
+                        else
+                            findEnd (i+2)
+                      | _ => findEnd (i+1)
+                end
+
+        val last = findEnd 1
+
+        fun unesc i =
+            if i >= last then
+                ""
+            else
+                let
+                    val ch = String.sub s i
+                in
+                    case ch of
+                        #"\\" =>
+                        if i+1 >= len then
+                            error <xml>JSON unescape: Bad escape sequence: {[s]}</xml>
+                        else
+                            String.str (String.sub s (i+1)) ^ unesc (i+2)
+                      | _ => String.str ch ^ unesc (i+1)
+                end
+    in
+        if len = 0 || String.sub s 0 <> #"\"" then
+            error <xml>JSON unescape: String doesn't start with double quote: {[s]}</xml>
+        else
+            (unesc 1, String.substring s {Start = last+1, Len = len-last-1})
+    end
+
+val json_string = {ToJson = escape,
+                   FromJson = unescape}
+
+fun numIn [a] (_ : read a) s : a * string =
+    let
+        val len = String.length s
+
+        fun findEnd i =
+            if i >= len then
+                i
+            else
+                let
+                    val ch = String.sub s i
+                in
+                    if Char.isDigit ch || ch = #"-" || ch = #"." || ch = #"E" || ch = #"e" then
+                        findEnd (i+1)
+                    else
+                        i
+                end
+
+        val last = findEnd 0
+    in
+        (readError (String.substring s {Start = 0, Len = last}), String.substring s {Start = last, Len = len-last})
+    end
+
+fun json_num [a] (_ : show a) (_ : read a) : json a = {ToJson = show,
+                                                       FromJson = numIn}
+
+val json_int = json_num
+val json_float = json_num
+
+val json_bool = {ToJson = fn b => if b then "true" else "false",
+                 FromJson = fn s => if String.isPrefix {Full = s, Prefix = "true"} then
+                                        (True, String.substring s {Start = 4, Len = String.length s - 4})
+                                    else if String.isPrefix {Full = s, Prefix = "false"} then
+                                        (False, String.substring s {Start = 5, Len = String.length s - 5})
+                                    else
+                                        error <xml>JSON: bad boolean string: {[s]}</xml>}
+
+fun json_option [a] (j : json a) : json (option a) =
+    {ToJson = fn v => case v of
+                          None => "null"
+                        | Some v => j.ToJson v,
+     FromJson = fn s => if String.isPrefix {Full = s, Prefix = "null"} then
+                            (None, String.substring s {Start = 4, Len = String.length s - 4})
+                        else
+                            let
+                                val (v, s') = j.FromJson s
+                            in
+                                (Some v, s')
+                            end}
+
+fun json_list [a] (j : json a) : json (list a) =
+    let
+        fun toJ' (ls : list a) : string =
+            case ls of
+                [] => ""
+              | x :: ls => "," ^ toJson j x ^ toJ' ls
+
+        fun toJ (x : list a) : string =
+            case x of
+                [] => "[]"
+              | x :: [] => "[" ^ toJson j x ^ "]"
+              | x :: ls => "[" ^ toJson j x ^ toJ' ls ^ "]"
+
+        fun fromJ (s : string) : list a * string =
+            let
+                fun fromJ' (s : string) : list a * string =
+                    if String.length s = 0 then
+                        error <xml>JSON list doesn't end with ']'</xml>
+                    else
+                        let
+                            val ch = String.sub s 0
+                        in
+                            case ch of
+                                #"]" => ([], String.substring s {Start = 1, Len = String.length s - 1})
+                              | _ =>
+                                let
+                                    val (x, s') = j.FromJson s
+                                    val s' = skipSpaces s'
+                                    val s' = if String.length s' = 0 then
+                                                 error <xml>JSON list doesn't end with ']'</xml>
+                                             else if String.sub s' 0 = #"," then
+                                                 skipSpaces (String.substring s' {Start = 1, Len = String.length s' - 1})
+                                             else
+                                                 s'
+
+                                    val (ls, s'') = fromJ' s'
+                                in
+                                    (x :: ls, s'')
+                                end
+                        end
+            in
+                if String.length s = 0 || String.sub s 0 <> #"[" then
+                    error <xml>JSON list doesn't start with '[': {[s]}</xml>
+                else
+                    fromJ' (skipSpaces (String.substring s {Start = 1, Len = String.length s - 1}))
+            end
+    in
+        {ToJson = toJ,
+         FromJson = fromJ}
+    end
+
+fun json_record [ts ::: {Type}] (fl : folder ts) (jss : $(map json ts)) (names : $(map (fn _ => string) ts)) : json $ts =
+    {ToJson = fn r => "{" ^ @foldR3 [json] [fn _ => string] [ident] [fn _ => string]
+                             (fn [nm ::_] [t ::_] [r ::_] [[nm] ~ r] (j : json t) name v acc =>
+                                 escape name ^ ":" ^ j.ToJson v ^ (case acc of
+                                                                       "" => ""
+                                                                     | _ => "," ^ acc))
+                             "" fl jss names r ^ "}",
+     FromJson = fn s =>
+                   let
+                       fun fromJ s (r : $(map option ts)) : $(map option ts) * string =
+                           if String.length s = 0 then
+                               error <xml>JSON object doesn't end in brace</xml>
+                           else if String.sub s 0 = #"}" then
+                               (r, String.substring s {Start = 1, Len = String.length s - 1})
+                           else let
+                                   val (name, s') = unescape s
+                                   val s' = skipSpaces s'
+                                   val s' = if String.length s' = 0 || String.sub s' 0 <> #":" then
+                                                error <xml>No colon after JSON object field name</xml>
+                                            else
+                                                skipSpaces (String.substring s' {Start = 1, Len = String.length s' - 1})
+
+                                   val (r, s') = @foldR2 [json] [fn _ => string] [fn ts => $(map option ts) -> $(map option ts) * string]
+                                                  (fn [nm ::_] [t ::_] [r ::_] [[nm] ~ r] (j : json t) name' acc r =>
+                                                      if name = name' then
+                                                          let
+                                                              val (v, s') = j.FromJson s'
+                                                          in
+                                                              (r -- nm ++ {nm = Some v}, s')
+                                                          end
+                                                      else
+                                                          let
+                                                              val (r', s') = acc (r -- nm)
+                                                          in
+                                                              (r' ++ {nm = r.nm}, s')
+                                                          end)
+                                                  (fn _ => error <xml>Unknown JSON object field name {[name]}</xml>)
+                                                  fl jss names r
+
+                                   val s' = skipSpaces s'
+                                   val s' = if String.length s' <> 0 && String.sub s' 0 = #"," then
+                                                skipSpaces (String.substring s' {Start = 1, Len = String.length s' - 1})
+                                            else
+                                                s'
+                               in
+                                   fromJ s' r
+                               end
+                   in
+                       if String.length s = 0 || String.sub s 0 <> #"{" then
+                           error <xml>JSON record doesn't begin with brace</xml>
+                       else
+                           let
+                               val (r, s') = fromJ (skipSpaces (String.substring s {Start = 1, Len = String.length s - 1}))
+                                                   (@map0 [option] (fn [t ::_] => None) fl)
+                           in
+                               (@map2 [option] [fn _ => string] [ident] (fn [t] (v : option t) name =>
+                                                                            case v of
+                                                                                None => error <xml>Missing JSON object field {[name]}</xml>
+                                                                              | Some v => v) fl r names, s')
+                           end
+                   end}
+
+fun json_variant [ts ::: {Type}] (fl : folder ts) (jss : $(map json ts)) (names : $(map (fn _ => string) ts)) : json (variant ts) =
+    {ToJson = fn r => let val jnames = @map2 [json] [fn _ => string] [fn x => json x * string]
+                                     (fn [t] (j : json t) (name : string) => (j, name)) fl jss names
+                      in @Variant.destrR [ident] [fn x => json x * string]
+                          (fn [p ::_] (v : p) (j : json p, name : string) =>
+                            "{" ^ escape name ^ ":" ^ j.ToJson v ^ "}") fl r jnames
+                      end,
+     FromJson = fn s =>
+                   if String.length s = 0 || String.sub s 0 <> #"{" then
+                       error <xml>JSON variant doesn't begin with brace</xml>
+                   else
+                       let
+                           val (name, s') = unescape (skipSpaces (String.suffix s 1))
+                           val s' = skipSpaces s'
+                           val s' = if String.length s' = 0 || String.sub s' 0 <> #":" then
+                                        error <xml>No colon after JSON object field name</xml>
+                                    else
+                                        skipSpaces (String.substring s' {Start = 1, Len = String.length s' - 1})
+
+                           val (r, s') = (@foldR2 [json] [fn _ => string]
+                                            [fn ts => ts' :: {Type} -> [ts ~ ts'] => variant (ts ++ ts') * string]
+                                            (fn [nm ::_] [t ::_] [rest ::_] [[nm] ~ rest] (j : json t) name'
+                                             (acc : ts' :: {Type} -> [rest ~ ts'] => variant (rest ++ ts') * string) [fwd ::_] [[nm = t] ++ rest ~ fwd] =>
+                                                if name = name'
+                                                    then
+                                                        let val (v, s') = j.FromJson s'
+                                                        in (make [nm] v, s')
+                                                        end
+                                                    else acc [fwd ++ [nm = t]]
+                                            )
+                                            (fn [fwd ::_] [[] ~ fwd] => error <xml>Unknown JSON object variant name {[name]}</xml>)
+                                            fl jss names) [[]] !
+
+                           val s' = skipSpaces s'
+                           val s' = if String.length s' <> 0 && String.sub s' 0 = #"," then
+                                        skipSpaces (String.substring s' {Start = 1, Len = String.length s' - 1})
+                                    else
+                                        s'
+                       in
+                           if String.length s' = 0 then
+                               error <xml>JSON object doesn't end in brace</xml>
+                           else if String.sub s' 0 = #"}" then
+                               (r, String.substring s' {Start = 1, Len = String.length s' - 1})
+                           else error <xml>Junk after JSON value in object</xml>
+                       end
+                   }
+
+val json_unit : json unit = json_record {} {}
+
+functor Recursive (M : sig
+                       con t :: Type -> Type
+                       val json_t : a ::: Type -> json a -> json (t a)
+                   end) = struct
+    open M
+
+    datatype r = Rec of t r
+
+    fun rTo (Rec x) = (json_t {ToJson = rTo,
+                               FromJson = fn _ => error <xml>Tried to FromJson in ToJson!</xml>}).ToJson x
+
+    fun rFrom s =
+        let
+            val (x, s') = (json_t {ToJson = fn _ => error <xml>Tried to ToJson in FromJson!</xml>,
+                                   FromJson = rFrom}).FromJson s
+        in
+            (Rec x, s')
+        end
+
+    val json_r = {ToJson = rTo, FromJson = rFrom}
+end

+ 31 - 0
UrWeb/meta/json.urs

@@ -0,0 +1,31 @@
+(** The JSON text-based serialization format *)
+
+class json
+
+val toJson : a ::: Type -> json a -> a -> string
+val fromJson : a ::: Type -> json a -> string -> a
+val fromJson' : a ::: Type -> json a -> string -> a * string
+
+val mkJson : a ::: Type -> {ToJson : a -> string,
+                            FromJson : string -> a * string} -> json a
+
+val json_string : json string
+val json_int : json int
+val json_float : json float
+val json_bool : json bool
+val json_option : a ::: Type -> json a -> json (option a)
+val json_list : a ::: Type -> json a -> json (list a)
+
+val json_record : ts ::: {Type} -> folder ts -> $(map json ts) -> $(map (fn _ => string) ts) -> json $ts
+val json_variant : ts ::: {Type} -> folder ts -> $(map json ts) -> $(map (fn _ => string) ts) -> json (variant ts)
+
+val json_unit : json unit
+
+functor Recursive (M : sig
+                       con t :: Type -> Type
+                       val json_t : a ::: Type -> json a -> json (t a)
+                   end) : sig
+    datatype r = Rec of M.t r
+
+    val json_r : json r
+end

+ 12 - 0
UrWeb/meta/lib.urp

@@ -0,0 +1,12 @@
+$/char
+$/string
+$/option
+incl
+mem
+eq
+record
+variant
+json
+sql
+parse
+html

+ 38 - 0
UrWeb/meta/mem.ur

@@ -0,0 +1,38 @@
+con mem' = K ==> fn (nm :: Name) (t :: K) (r :: {K}) (r' :: {K}) =>
+    [[nm] ~ r'] => {Expose : f :: ({K} -> Type) -> f r -> f ([nm = t] ++ r'),
+                    Hide : f :: ({K} -> Type) -> f ([nm = t] ++ r') -> f r}
+
+con mem = K ==> fn (nm :: Name) (t :: K) (r :: {K}) =>
+                   tp :: Type -> (r' :: {K} -> [[nm] ~ r'] => mem' nm t r r' -> tp) -> tp
+
+fun mem [K] [nm :: Name] [t :: K] [r :: {K}] [[nm] ~ r] =
+    fn [tp :: Type] (f : r' :: {K} -> [[nm] ~ r'] => mem' nm t ([nm = t] ++ r) r' -> tp) =>
+       f [r] (fn [[nm] ~ r] => {Expose = fn [f :: {K} -> Type] x => x,
+                                Hide = fn [f :: {K} -> Type] x => x})
+
+fun mp [K] [K2] [f :: K -> K2] [nm ::: Name] [t ::: K] [r ::: {K}] (m : mem nm t r) =
+    m [mem nm (f t) (map f r)] (fn [r' :: {K}] [[nm] ~ r'] (m' : mem' nm t r r') =>
+                                fn [tp :: Type] (f : r' :: {K2} -> [[nm] ~ r'] =>
+                                                 mem' nm (f t) (map f r) r' -> tp) =>
+                                   f [map f r'] (fn [[nm] ~ map f r'] =>
+                                                    {Expose = fn [f' :: {K2} -> Type] x =>
+                                                                 m'.Expose [fn r => f' (map f r)] x,
+                                                     Hide = fn [f' :: {K2} -> Type] x =>
+                                                               m'.Hide [fn r => f' (map f r)] x}))
+
+fun proj [nm ::: Name] [t ::: Type] [r ::: {Type}] (m : mem nm t r) (r : $r) =
+    m [t] (fn [r' :: {Type}] [[nm] ~ r'] (m' : mem' nm t r r') =>
+              (m'.Expose [fn r => $r] r).nm)
+
+fun replace [nm ::: Name] [t ::: Type] [r ::: {Type}] (m : mem nm t r) (r : $r) (v : t) =
+    m [$r] (fn [r' :: {Type}] [[nm] ~ r'] (m' : mem' nm t r r') =>
+               m'.Hide [fn r => $r] (m'.Expose [fn r => $r] r -- nm ++ {nm = v}))
+
+fun fold [K] [tf :: ({K} -> Type)] [r ::: {K}]
+    (f : nm :: Name -> v :: K -> r' :: {K} -> [[nm] ~ r']
+     => mem nm v r -> tf r' -> tf ([nm = v] ++ r'))
+    (i : tf []) (fl : folder r) =
+    @@Incl.fold [tf] [r]
+      (fn [nm :: Name] [v :: K] [r' :: {K}] [[nm] ~ r'] (i : Incl.incl ([nm = v] ++ r') r) acc =>
+          f [nm] [v] [r'] (Incl.inv1 [nm] [v] [r'] [r] [mem] i mem) acc)
+      i fl

+ 15 - 0
UrWeb/meta/mem.urs

@@ -0,0 +1,15 @@
+(** A record membership predicate *)
+
+con mem :: K --> Name -> K -> {K} -> Type
+
+val mem : K --> nm :: Name -> t :: K -> r :: {K} -> [[nm] ~ r] => mem nm t ([nm = t] ++ r)
+val mp : K --> K2 --> f :: (K -> K2) -> nm ::: Name -> t ::: K -> r ::: {K} -> mem nm t r -> mem nm (f t) (map f r)
+
+val proj : nm ::: Name -> t ::: Type -> r ::: {Type} -> mem nm t r -> $r -> t
+val replace : nm ::: Name -> t ::: Type -> r ::: {Type} -> mem nm t r -> $r -> t -> $r
+
+val fold : K --> tf :: ({K} -> Type) -> r ::: {K}
+           -> (nm :: Name -> v :: K -> r' :: {K} -> [[nm] ~ r']
+               => mem nm v r -> tf r' -> tf ([nm = v] ++ r'))
+           -> tf []
+           -> folder r -> tf r

+ 5 - 0
UrWeb/meta/parse.ur

@@ -0,0 +1,5 @@
+(** Datatypes for describing parse results *)
+
+datatype parse a =
+         Success of a
+       | Failure of string

+ 18 - 0
UrWeb/meta/record.ur

@@ -0,0 +1,18 @@
+fun numFields [r ::: {Type}] (fl : folder r) (r : $r) : int =
+    @fold [fn _ => int] (fn [nm ::_] [u ::_] [r ::_] [[nm] ~ r] acc => acc+1) 0 fl
+
+fun mem [a ::: Type] [ns ::: {Unit}] (_ : eq a) (fl : folder ns) (x : a) (r : $(mapU a ns)) : bool =
+    @foldUR [a] [fn _ => bool]
+     (fn [nm ::_] [r ::_] [[nm] ~ r] y acc =>
+         acc || x = y)
+     False fl r
+
+fun equal [ts ::: {Type}] (eqs : $(map eq ts)) (fl : folder ts) (r1 : $ts) (r2 : $ts) : bool =
+    @foldR3 [eq] [ident] [ident] [fn _ => bool]
+     (fn [nm ::_] [t ::_] [r ::_] [[nm] ~ r] isEq x y acc =>
+         acc && @eq isEq x y)
+     True fl eqs r1 r2
+
+fun ap [K] [tf1 :: K -> Type] [tf2 :: K -> Type]
+       [r ::: {K}] (fl : folder r) (fs : $(map (fn t => tf1 t -> tf2 t) r)) (xs : $(map tf1 r))
+  = @map2 [fn t => tf1 t -> tf2 t] [fn t => tf1 t] [tf2] (fn [t] f x => f x) fl fs xs

+ 14 - 0
UrWeb/meta/record.urs

@@ -0,0 +1,14 @@
+val numFields : r ::: {Type} -> folder r -> $r -> int
+
+val mem : a ::: Type -> ns ::: {Unit} -> eq a -> folder ns -> a -> $(mapU a ns) -> bool
+(* Is a value found in a record? *)
+
+val equal : ts ::: {Type} -> $(map eq ts) -> folder ts -> $ts -> $ts -> bool
+(* Are two records equal? *)
+
+(* Analogous to applicative ap e.g. <*>, of type [f (a -> b) -> f a -> f b] *)
+val ap : K --> tf1 :: (K -> Type) -> tf2 :: (K -> Type)
+         -> r ::: {K} -> folder r
+         -> $(map (fn t => tf1 t -> tf2 t) r)
+         -> $(map tf1 r)
+         -> $(map tf2 r)

+ 75 - 0
UrWeb/meta/sql.ur

@@ -0,0 +1,75 @@
+fun sqexps [env] [fields] (fl : folder fields) (inj : $(map sql_injectable fields)) (r : $fields) =
+    @map2 [sql_injectable] [ident] [sql_exp env [] []]
+     (fn [t] => @sql_inject)
+     fl inj r
+
+fun selector [tn :: Name] [fs] [ofs] [fs ~ ofs] (fl : folder fs) (m : $(map sql_injectable fs)) (r : $fs)
+    : sql_exp [tn = ofs ++ fs] [] [] bool =
+    @foldR2 [sql_injectable] [ident]
+     [fn key => rest :: {Type} -> [rest ~ key] => sql_exp [tn = key ++ rest] [] [] bool]
+     (fn [nm :: Name] [t :: Type] [key :: {Type}] [[nm] ~ key]
+                      (inj : sql_injectable t) (v : t)
+                      (exp : rest :: {Type} -> [rest ~ key] => sql_exp [tn = key ++ rest] [] [] bool)
+                      [rest :: {Type}] [rest ~ [nm = t] ++ key] =>
+         (WHERE {{tn}}.{nm} = {@sql_inject inj v} AND {exp [[nm = t] ++ rest]}))
+     (fn [rest :: {Type}] [rest ~ []] => (WHERE TRUE))
+     fl m r [_] !
+
+fun joiner [tn1 :: Name] [tn2 :: Name] [fs] [ofs1] [ofs2] [[tn1] ~ [tn2]] [fs ~ ofs1] [fs ~ ofs2]
+           (fl : folder fs) : sql_exp [tn1 = ofs1 ++ fs, tn2 = ofs2 ++ fs] [] [] bool =
+    @fold
+     [fn key => rest1 :: {Type} -> rest2 :: {Type} -> [rest1 ~ key] => [rest2 ~ key] => sql_exp [tn1 = key ++ rest1, tn2 = key ++ rest2] [] [] bool]
+     (fn [nm :: Name] [t :: Type] [key :: {Type}] [[nm] ~ key]
+                      (exp : rest1 :: {Type} -> rest2 :: {Type} -> [rest1 ~ key] => [rest2 ~ key]
+                       => sql_exp [tn1 = key ++ rest1, tn2 = key ++ rest2] [] [] bool)
+                      [rest1 :: {Type}] [rest2 :: {Type}] [rest1 ~ [nm = t] ++ key] [rest2 ~ [nm = t] ++ key] =>
+         (WHERE {{tn1}}.{nm} = {{tn2}}.{nm} AND {exp [[nm = t] ++ rest1] [[nm = t] ++ rest2]}))
+     (fn [rest1 :: {Type}] [rest2 :: {Type}] [rest1 ~ []] [rest2 ~ []] => (WHERE TRUE))
+     fl [_] [_] ! !
+
+fun insertIfMissing [keyCols ::: {Type}] [otherCols ::: {Type}] [otherKeys ::: {{Unit}}]
+                    [keyCols ~ otherCols] [[Pkey] ~ otherKeys]
+                    (kfl : folder keyCols) (kinj : $(map sql_injectable keyCols))
+                    (ofl : folder otherCols) (oinj : $(map sql_injectable otherCols))
+                    (t : sql_table (keyCols ++ otherCols) ([Pkey = map (fn _ => ()) keyCols] ++ otherKeys))
+                    (vs : $(keyCols ++ otherCols))
+    : transaction bool =
+    alreadyThere <- oneRowE1 (SELECT COUNT( * ) > 0
+                              FROM t
+                              WHERE {@selector [#T] ! kfl kinj (vs --- _)});
+    if alreadyThere then
+        return False
+    else
+        dml (insert t (@sqexps kfl kinj (vs --- _)
+                        ++ @sqexps ofl oinj (vs --- _)));
+        return True
+
+fun deleteByKey [keyCols ::: {Type}] [otherCols ::: {Type}] [otherKeys ::: {{Unit}}]
+    [keyCols ~ otherCols] [[Pkey] ~ otherKeys]
+    (kfl : folder keyCols) (kinj : $(map sql_injectable keyCols))
+    (t : sql_table (keyCols ++ otherCols) ([Pkey = map (fn _ => ()) keyCols] ++ otherKeys))
+    (vs : $keyCols) =
+    dml (delete t (@selector [#T] ! kfl kinj vs))
+
+fun lookup [keyCols ::: {Type}] [otherCols ::: {Type}] [otherKeys ::: {{Unit}}]
+           [keyCols ~ otherCols] [[Pkey] ~ otherKeys]
+           (kfl : folder keyCols) (kinj : $(map sql_injectable keyCols))
+           (t : sql_table (keyCols ++ otherCols) ([Pkey = map (fn _ => ()) keyCols] ++ otherKeys))
+           (vs : $keyCols)
+    : transaction (option $otherCols) =
+      oneOrNoRows1 (SELECT t.{{otherCols}}
+                    FROM t
+                    WHERE {@selector [#T] ! kfl kinj (vs --- _)})
+
+fun listify [lead :: Name] [cols ::: {Type}] [rest ::: {{Type}}] [[lead] ~ rest]
+    (fl : folder cols) (eqs : $(map eq cols)) (q : sql_query [] [] ([lead = cols] ++ rest) []) =
+    query q
+    (fn r acc =>
+        return (case acc of
+                    [] => (r.lead, (r -- lead) :: []) :: []
+                  | (key, ls) :: acc' =>
+                    if @Record.equal eqs fl r.lead key then
+                        (key, (r -- lead) :: ls) :: acc'
+                    else
+                        (r.lead, (r -- lead) :: []) :: acc))
+    []

+ 47 - 0
UrWeb/meta/sql.urs

@@ -0,0 +1,47 @@
+(** Common metaprogramming patterns for SQL syntax construction *)
+
+val sqexps : env ::: {{Type}} -> fields ::: {Type} -> folder fields -> $(map sql_injectable fields)
+             -> $fields -> $(map (sql_exp env [] []) fields)
+(* Convert a record of Ur values into a record of SQL expressions *)
+
+val selector : tn :: Name -> fs ::: {Type} -> ofs ::: {Type} -> [fs ~ ofs]
+               => folder fs -> $(map sql_injectable fs) -> $fs
+               -> sql_exp [tn = ofs ++ fs] [] [] bool
+(* Build a boolean SQL expression expressing equality of some fields of a table
+ * row with a record of Ur values *)
+
+val joiner : tn1 :: Name -> tn2 :: Name -> fs ::: {Type} -> ofs1 ::: {Type} -> ofs2 ::: {Type}
+             -> [[tn1] ~ [tn2]] => [fs ~ ofs1] => [fs ~ ofs2]
+               => folder fs
+               -> sql_exp [tn1 = ofs1 ++ fs, tn2 = ofs2 ++ fs] [] [] bool
+(* Declare equality of same-named columns from two tables. *)
+
+val insertIfMissing : keyCols ::: {Type} -> otherCols ::: {Type} -> otherKeys ::: {{Unit}}
+                      -> [keyCols ~ otherCols] => [[Pkey] ~ otherKeys]
+                      => folder keyCols -> $(map sql_injectable keyCols)
+                      -> folder otherCols -> $(map sql_injectable otherCols)
+                      -> sql_table (keyCols ++ otherCols) ([Pkey = map (fn _ => ()) keyCols] ++ otherKeys)
+                      -> $(keyCols ++ otherCols)
+                      -> transaction bool
+(* Insert a row into an SQL table if its key isn't already present, returning [False] iff the key was already present *)
+
+val deleteByKey : keyCols ::: {Type} -> otherCols ::: {Type} -> otherKeys ::: {{Unit}}
+                  -> [keyCols ~ otherCols] => [[Pkey] ~ otherKeys]
+                  => folder keyCols -> $(map sql_injectable keyCols)
+                  -> sql_table (keyCols ++ otherCols) ([Pkey = map (fn _ => ()) keyCols] ++ otherKeys)
+                  -> $keyCols
+                  -> transaction {}
+(* Delete a row from a table by matching its primary key against a given record. *)
+
+val lookup : keyCols ::: {Type} -> otherCols ::: {Type} -> otherKeys ::: {{Unit}}
+             -> [keyCols ~ otherCols] => [[Pkey] ~ otherKeys]
+             => folder keyCols -> $(map sql_injectable keyCols)
+             -> sql_table (keyCols ++ otherCols) ([Pkey = map (fn _ => ()) keyCols] ++ otherKeys)
+             -> $keyCols -> transaction (option $otherCols)
+(* Get the further columns associated with a table key. *)
+
+val listify : lead :: Name -> cols ::: {Type} -> rest ::: {{Type}} -> [[lead] ~ rest]
+              => folder cols -> $(map eq cols)
+              -> sql_query [] [] ([lead = cols] ++ rest) []
+              -> transaction (list ($cols * list $(map (fn ts => $ts) rest)))
+(* Shrink a set of table rows by summarizing into lists, keyed off of a lead table *)

+ 145 - 0
UrWeb/meta/variant.ur

@@ -0,0 +1,145 @@
+fun read [r ::: {Unit}] [t ::: Type] (fl : folder r) (r : $(mapU t r)) (v : variant (mapU {} r)) : t =
+    match v
+    (@fold [fn r => r' :: {Unit} -> [r ~ r'] => $(mapU t (r ++ r')) -> $(mapU ({} -> t) r)]
+     (fn [nm :: Name] [u::_] [us::_] [[nm] ~ us] (cs : r' :: {Unit} -> [us ~ r'] => $(mapU t (us ++ r')) -> _) [r'::_] [[nm = u] ++ us ~ r'] r =>
+         {nm = fn () => r.nm} ++ cs [[nm = u] ++ r'] r)
+     (fn [r'::_] [[] ~ r'] _ => {}) fl [[]] ! r)
+
+fun write [r ::: {Unit}] [t ::: Type] (fl : folder r) (r : $(mapU t r)) (v : variant (mapU {} r)) (x : t) : $(mapU t r) =
+    match v
+    (@fold [fn r => r' :: {Unit} -> [r ~ r'] => $(mapU t (r ++ r')) -> $(mapU ({} -> $(mapU t (r ++ r'))) r)]
+      (fn [nm :: Name] [u::_] [us::_] [[nm] ~ us]
+          (cs : r' :: {Unit} -> [us ~ r'] => $(mapU t (us ++ r')) -> $(mapU ({} -> $(mapU t (us ++ r'))) us))
+          [r'::_] [[nm = u] ++ us ~ r'] r =>
+          {nm = fn () => r -- nm ++ {nm = x}} ++ cs [[nm = u] ++ r'] r)
+      (fn [r'::_] [[] ~ r'] _ => {}) fl [[]] ! r)
+
+fun search [r] [t] (f : variant (mapU {} r) -> option t) (fl : folder r) : option t =
+    @fold [fn r => r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> option t) -> option t]
+    (fn [nm :: Name] [u ::_] [r ::_] [[nm] ~ r]
+                     (acc : r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> option t) -> option t)
+                     [r' ::_] [[nm] ++ r ~ r'] f' =>
+        case f' (make [nm] {}) of
+            None => acc [[nm] ++ r'] f'
+          | v => v)
+    (fn [r' ::_] [[] ~ r'] _ => None) fl [[]] ! f
+
+fun find [r] (f : variant (mapU {} r) -> bool) (fl : folder r) : option (variant (mapU {} r)) =
+    @search (fn v => if f v then Some v else None) fl
+
+fun test [nm :: Name] [t ::: Type] [ts ::: {Type}] [[nm] ~ ts] (fl : folder ([nm = t] ++ ts))
+          (v : variant ([nm = t] ++ ts)) : option t =
+    match v ({nm = Some}
+                 ++ (@map0 [fn t' => t' -> option t] (fn [t' :: Type] _ => None) fl -- nm))
+
+fun testLess [nm :: Name] [t ::: Type] [ts ::: {Type}] [[nm] ~ ts] (fl : folder ts) (v : variant ([nm = t] ++ ts)) : option t =
+    match v ({nm = Some}
+                 ++ @map0 [fn t' => t' -> option t] (fn [t' :: Type] _ => None) fl)
+
+fun weaken [r1 ::: {Type}] [r2 ::: {Type}] [r1 ~ r2] (fl : folder r1) (v : variant r1) : variant (r1 ++ r2) =
+    match v
+    (@fold [fn r => r' :: {Type} -> [r ~ r'] => $(map (fn t => t -> variant (r ++ r')) r)]
+      (fn [nm :: Name] [t ::_] [r ::_] [[nm] ~ r] (acc : r' :: {Type} -> [r ~ r'] => $(map (fn t => t -> variant (r ++ r')) r)) [r'::_] [[nm = t] ++ r ~ r'] =>
+          {nm = make [nm]} ++ acc [[nm = t] ++ r'])
+      (fn [r'::_] [[] ~ r'] => {}) fl [r2] !)
+
+fun eq [r] (fl : folder r) (v1 : variant (mapU {} r)) (v2 : variant (mapU {} r)) : bool =
+    match v1
+    (@fold [fn r => r' :: {Unit} -> [r ~ r'] => folder (r ++ r') -> variant (mapU {} (r ++ r')) -> $(mapU ({} -> bool) r)]
+     (fn [nm ::_] [u ::_] [r ::_] [[nm] ~ r]
+         (acc : r' :: {Unit} -> [r ~ r'] => folder (r ++ r') -> variant (mapU {} (r ++ r')) -> $(mapU ({} -> bool) r))
+         [r' ::_] [[nm] ++ r ~ r'] (fl' : folder ([nm] ++ r ++ r')) v =>
+         {nm = fn () => Option.isSome (@test [nm] ! (@Folder.mp fl') v)}
+             ++ @acc [[nm] ++ r'] ! fl' v)
+     (fn [r' ::_] [[] ~ r'] _ _ => {}) fl [[]] ! fl v2)
+
+fun fold [r] [t] (fl : folder r) (f : variant (mapU {} r) -> t -> t) : t -> t =
+    @Top.fold [fn r => r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> t -> t) -> t -> t]
+    (fn [nm :: Name] [u ::_] [r ::_] [[nm] ~ r]
+                     (acc : r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> t -> t) -> t -> t)
+                     [r' ::_] [[nm] ++ r ~ r'] f' acc' =>
+        f' (make [nm] {}) (acc [[nm] ++ r'] f' acc'))
+    (fn [r' ::_] [[] ~ r'] _ x => x) fl [[]] ! f
+
+fun mp [r ::: {Unit}] [t ::: Type] (fl : folder r) (f : variant (mapU {} r) -> t) : $(mapU t r) =
+    @Top.fold [fn r => r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> t) -> $(mapU t r)]
+    (fn [nm :: Name] [u ::_] [r ::_] [[nm] ~ r]
+                     (acc : r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> t) -> $(mapU t r))
+                     [r' ::_] [[nm] ++ r ~ r'] f' =>
+        {nm = f' (make [nm] {})} ++ acc [[nm] ++ r'] f')
+    (fn [r' ::_] [[] ~ r'] _ => {}) fl [[]] ! f
+
+fun foldR [tr] [r] [t] (fl : folder r) (f : variant (mapU {} r) -> tr -> t -> t) (record : $(mapU tr r)) : t -> t =
+    @Top.foldUR [tr] [fn r => r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> tr -> t -> t) -> t -> t]
+    (fn [nm :: Name] [r ::_] [[nm] ~ r] (v : tr)
+                     (acc : r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> tr -> t -> t) -> t -> t)
+                     [r' ::_] [[nm] ++ r ~ r'] f' acc' =>
+        f' (make [nm] {}) v (acc [[nm] ++ r'] f' acc'))
+    (fn [r' ::_] [[] ~ r'] _ x => x) fl record [[]] ! f
+
+fun appR [m] (_ : monad m) [tr] [r] (fl : folder r) (f : variant (mapU {} r) -> tr -> m {}) (record : $(mapU tr r)) : m {} =
+    @foldR fl (fn var this acc => f var this; acc) record (return ())
+
+fun mapR [tr] [t] [r] (fl : folder r) (f : variant (mapU {} r) -> tr -> t) (record : $(mapU tr r)) : $(mapU t r) =
+    @Top.fold [fn r => r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> tr -> t) -> $(mapU tr r) -> $(mapU t r)]
+    (fn [nm :: Name] [u ::_] [r ::_] [[nm] ~ r]
+                     (acc : r' :: {Unit} -> [r ~ r'] => (variant (mapU {} (r ++ r')) -> tr -> t) -> $(mapU tr r) -> $(mapU t r))
+                     [r' ::_] [[nm] ++ r ~ r'] f' vs =>
+        {nm = f' (make [nm] {}) vs.nm} ++ acc [[nm] ++ r'] f' (vs -- nm))
+    (fn [r' ::_] [[] ~ r'] _ _ => {}) fl [[]] ! f record
+
+fun destrR [K] [f :: K -> Type] [fr :: K -> Type] [t ::: Type]
+    (f : p :: K -> f p -> fr p -> t)
+    [r ::: {K}] (fl : folder r) (v : variant (map f r)) (r : $(map fr r)) : t =
+    match v
+    (@Top.mp [fr] [fn p => f p -> t]
+     (fn [p] (m : fr p) (v : f p) => f [p] v m)
+     fl r)
+
+fun destr2R [K] [f1 :: K -> Type] [f2 :: K -> Type] [fr :: K -> Type] [t ::: Type]
+    (f : p :: K -> f1 p -> f2 p -> fr p -> t)
+    [r ::: {K}] (fl : folder r) (v1 : variant (map f1 r)) (v2 : variant (map f2 r)) (r : $(map fr r)) : option t =
+    match v1
+    (@Top.foldR [fr] [fn r => others :: {K} -> [others ~ r] =>
+                     folder (r ++ others)
+                     -> variant (map f2 (r ++ others))
+                     -> $(map (fn p => f1 p -> option t) r)]
+     (fn [nm ::_] [p ::_] [r ::_] [[nm] ~ r] (meta : fr p)
+         (acc : others :: {K} -> [others ~ r] =>
+          folder (r ++ others)
+          -> variant (map f2 (r ++ others))
+          -> $(map (fn p => f1 p -> option t) r))
+         [others :: {K}] [others ~ [nm = p] ++ r]
+         (fl : folder ([nm = p] ++ r ++ others))
+         (v2 : variant (map f2 ([nm = p] ++ r ++ others))) =>
+         {nm = fn x1 => match v2
+                        ({nm = fn x2 => Some (f [p] x1 x2 meta)}
+                             ++ (@map0 [fn p => f2 p -> option t] (fn [p' ::_] _ => None) fl -- nm))}
+             ++ @acc [[nm = p] ++ others] ! fl v2)
+     (fn [others ::_] [others ~ []] _ _ => {})
+     fl r [[]] ! fl v2)
+
+fun testEq [K] [f :: K -> Type] [nm :: Name] [t ::: K] [ts ::: {K}] [r ::: {K}] [[nm] ~ ts]
+    (pf : Eq.eq r ([nm = t] ++ ts)) (fl : folder r) (v : variant (map f r)) : option (f t) =
+  @test [nm] ! (@Folder.mp (@Eq.cast pf [folder] fl))
+   (Eq.cast pf [fn r => variant (map f r)] v)
+
+fun makeEq [K] [f :: K -> Type] [nm :: Name] [t ::: K] [ts ::: {K}] [r ::: {K}] [[nm] ~ ts]
+    (pf : Eq.eq r ([nm = t] ++ ts)) (x : f t) : variant (map f r) =
+  Eq.cast (Eq.sym pf) [fn r => variant (map f r)] (make [nm] x)
+
+con variantMake ts' ts = $(map (fn t => t -> variant ts') ts)
+con mkLabelsAccum r = s :: {Type} -> [r ~ s] => variantMake (r ++ s) r
+fun mkLabels [ts ::: {Type}] (fl : folder ts) : variantMake ts ts
+  = @Top.fold [mkLabelsAccum]
+          (fn [nm::_] [v::_] [r::_] [[nm] ~ r]
+              (k : mkLabelsAccum r)
+              [s::_] [[nm = v] ++ r ~ s] => k [[nm = v] ++ s] ++ {nm = make [nm]})
+          (fn [s::_] [[] ~ s] => {}) fl [[]] !
+
+con type_case ts t a = (a -> variant ts) -> a -> t
+
+fun declareCase [ts] [t] [a] (f : (a -> variant ts) -> a -> t) : type_case ts t a = f
+fun typeCase [ts] [t] (v : variant ts) (dstrs : $(map (type_case ts t) ts)) (fl : folder ts) : t
+(* Ur/Web not clever enough to calculate these folders, it seems *)
+  = match v (@Record.ap [fn a => a -> variant ts] [fn a => a -> t] fl dstrs (@mkLabels fl))

+ 83 - 0
UrWeb/meta/variant.urs

@@ -0,0 +1,83 @@
+(** Derived functions dealing with polymorphic variants *)
+
+val read : r ::: {Unit} -> t ::: Type -> folder r -> $(mapU t r) -> variant (mapU {} r) -> t
+val write : r ::: {Unit} -> t ::: Type -> folder r -> $(mapU t r) -> variant (mapU {} r) -> t -> $(mapU t r)
+
+val search : r ::: {Unit} -> t ::: Type -> (variant (mapU {} r) -> option t) -> folder r -> option t
+val find : r ::: {Unit} -> (variant (mapU {} r) -> bool) -> folder r -> option (variant (mapU {} r))
+
+val test : nm :: Name -> t ::: Type -> ts ::: {Type} -> [[nm] ~ ts] => folder ([nm = t] ++ ts)
+                                                                    -> variant ([nm = t] ++ ts) -> option t
+val testLess : nm :: Name -> t ::: Type -> ts ::: {Type} -> [[nm] ~ ts] => folder ts -> variant ([nm = t] ++ ts) -> option t
+
+val weaken : r1 ::: {Type} -> r2 ::: {Type} -> [r1 ~ r2] => folder r1
+             -> variant r1 -> variant (r1 ++ r2)
+
+val testEq : K --> f :: (K -> Type) -> nm :: Name -> t ::: K -> ts ::: {K} -> r ::: {K} -> [[nm] ~ ts] =>
+    Eq.eq r ([nm = t] ++ ts)
+    -> folder r
+    -> variant (map f r) -> option (f t)
+
+val eq : r ::: {Unit} -> folder r -> variant (mapU {} r) -> variant (mapU {} r) -> bool
+
+val makeEq : K --> f :: (K -> Type) -> nm :: Name -> t ::: K -> ts ::: {K} -> r ::: {K} -> [[nm] ~ ts] =>
+    Eq.eq r ([nm = t] ++ ts)
+    -> f t
+    -> variant (map f r)
+
+val mp : r ::: {Unit} -> t ::: Type -> folder r -> (variant (mapU {} r) -> t) -> $(mapU t r)
+
+val fold : r ::: {Unit} -> t ::: Type -> folder r -> (variant (mapU {} r) -> t -> t) -> t -> t
+
+val foldR : tr ::: Type -> r ::: {Unit} -> t ::: Type -> folder r -> (variant (mapU {} r) -> tr -> t -> t) -> $(mapU tr r) -> t -> t
+
+val appR : m ::: (Type -> Type) -> monad m
+           -> tr ::: Type -> r ::: {Unit} -> folder r -> (variant (mapU {} r) -> tr -> m {}) -> $(mapU tr r) -> m {}
+
+val mapR : tr ::: Type -> t ::: Type -> r ::: {Unit} -> folder r -> (variant (mapU {} r) -> tr -> t) -> $(mapU tr r) -> $(mapU t r)
+
+val destrR : K --> f :: (K -> Type) -> fr :: (K -> Type) -> t ::: Type
+             -> (p :: K -> f p -> fr p -> t)
+             -> r ::: {K} -> folder r -> variant (map f r) -> $(map fr r) -> t
+
+val destr2R : K --> f1 :: (K -> Type) -> f2 :: (K -> Type) -> fr :: (K -> Type) -> t ::: Type
+             -> (p :: K -> f1 p -> f2 p -> fr p -> t)
+             -> r ::: {K} -> folder r -> variant (map f1 r) -> variant (map f2 r) -> $(map fr r) -> option t
+
+(* Metaprogrammed type-directed case-match.
+
+This uses a combination of type classes and metaprogramming to make
+it easy to write case-matches on very large variants with many
+similar elements.  Here's how you use it:
+
+    1. For every type in the variant, write a local typeclass function
+       which reduces it to t, and register as such using the 'declareCase'
+       function in the module you created.
+
+            let val empty = declareCase (fn _ (_ : int) => True)
+
+       These functions also take an initial argument, which has
+       type [a -> variant ts]; e.g. you can use this to create
+       a new copy of the variant with different values!
+       Make sure you specify type signatures on the argument [t]
+       so that we can identify who this typeclass is for.  (If you
+       use type classes to construct the return value, you may
+       also need to declare the return type explicitly.)
+
+    2. Do the match using 'typeCase':
+
+            typeCase t
+
+       If you need to override specific constructors, use this idiom:
+
+            @typeCase t (_ ++ {
+                YourConstr = declareCase (fn _ _ => ...)
+            }) _
+
+How does it work?  Very simple: it uses local typeclasses + Ur/Web's
+support for automatically instantiating records of typeclasses.
+*)
+
+class type_case :: {Type} -> Type -> Type -> Type
+val declareCase : ts ::: {Type} -> t ::: Type -> a ::: Type -> ((a -> variant ts) -> a -> t) -> type_case ts t a
+val typeCase : ts ::: {Type} -> t ::: Type -> variant ts -> $(map (type_case ts t) ts) -> folder ts -> t

+ 27 - 0
UrWeb/setup.py

@@ -0,0 +1,27 @@
+import subprocess
+import os
+
+def start(args, logfile, errfile):
+  subprocess.check_call("urweb bench", shell=True, cwd="UrWeb", stderr=errfile, stdout=logfile)
+
+  threads = str(args.max_threads)
+  conn_string = ('dbname=hello_world '
+                'user=benchmarkdbuser '
+                'password=benchmarkdbpass '
+                'host=' + args.database_host)
+  env = {'URWEB_PQ_CON': conn_string}
+  subprocess.Popen("./bench.exe -t " + threads,
+                   env=env, shell=True, cwd="UrWeb", stderr=errfile, stdout=logfile)
+  return 0
+
+def stop(logfile, errfile):
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'bench.exe' in line:
+      try:
+        pid = int(line.split(None, 2)[1])
+        os.kill(pid, 9)
+      except OSError:
+        pass
+  return 0

+ 1 - 0
UrWeb/source_code

@@ -0,0 +1 @@
+./UrWeb/bench.ur

+ 3 - 0
UrWeb/world.sql

@@ -0,0 +1,3 @@
+CREATE TABLE uw_Bench_world AS SELECT uw_id::int8, trunc(random()*9999+1)::int8 AS uw_randomnumber FROM generate_series(1,10000) AS uw_id;
+ALTER TABLE uw_Bench_world ADD PRIMARY KEY (uw_id);
+ALTER TABLE uw_Bench_world ALTER COLUMN uw_randomnumber SET NOT NULL;

+ 4 - 4
aspnet-stripped/setup_iis.py

@@ -3,20 +3,20 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   if os.name != 'nt':
   if os.name != 'nt':
     return 1
     return 1
   
   
   try:
   try:
     setup_util.replace_text("aspnet-stripped/src/Web.config", "localhost", args.database_host)
     setup_util.replace_text("aspnet-stripped/src/Web.config", "localhost", args.database_host)
-    subprocess.check_call("powershell -Command .\\setup_iis.ps1 start", cwd="aspnet-stripped")
+    subprocess.check_call("powershell -Command .\\setup_iis.ps1 start", cwd="aspnet-stripped", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name != 'nt':
   if os.name != 'nt':
     return 0
     return 0
   
   
-  subprocess.check_call("powershell -Command .\\setup_iis.ps1 stop", cwd="aspnet-stripped")
+  subprocess.check_call("powershell -Command .\\setup_iis.ps1 stop", cwd="aspnet-stripped", stderr=errfile, stdout=logfile)
   return 0
   return 0

+ 4 - 4
aspnet/setup_iis.py

@@ -3,20 +3,20 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   if os.name != 'nt':
   if os.name != 'nt':
     return 1
     return 1
   
   
   try:
   try:
     setup_util.replace_text("aspnet/src/Web.config", "localhost", args.database_host)
     setup_util.replace_text("aspnet/src/Web.config", "localhost", args.database_host)
-    subprocess.check_call("powershell -Command .\\setup_iis.ps1 start", cwd="aspnet")
+    subprocess.check_call("powershell -Command .\\setup_iis.ps1 start", cwd="aspnet", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name != 'nt':
   if os.name != 'nt':
     return 0
     return 0
   
   
-  subprocess.check_call("powershell -Command .\\setup_iis.ps1 stop", cwd="aspnet")
+  subprocess.check_call("powershell -Command .\\setup_iis.ps1 stop", cwd="aspnet", stderr=errfile, stdout=logfile)
   return 0
   return 0

+ 10 - 10
aspnet/setup_nginx.py

@@ -6,7 +6,7 @@ import os
 root = os.getcwd() + "/aspnet"
 root = os.getcwd() + "/aspnet"
 app = root + "/src"
 app = root + "/src"
 
 
-def start(args):
+def start(args, logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
     return 1
     return 1
   
   
@@ -14,28 +14,28 @@ def start(args):
 
 
   try:
   try:
     # build
     # build
-    subprocess.check_call("rm -rf bin obj", shell=True, cwd=app)
-    subprocess.check_call("xbuild /p:Configuration=Release", shell=True, cwd=app)
-    subprocess.check_call("sudo chown -R $USER:$USER /usr/local/etc/mono", shell=True)
+    subprocess.check_call("rm -rf bin obj", shell=True, cwd=app, stderr=errfile, stdout=logfile)
+    subprocess.check_call("xbuild /p:Configuration=Release", shell=True, cwd=app, stderr=errfile, stdout=logfile)
+    subprocess.check_call("sudo chown -R $USER:$USER /usr/local/etc/mono", shell=True, stderr=errfile, stdout=logfile)
     
     
     # nginx
     # nginx
     workers = 'worker_processes ' + str(args.max_threads) + ';'
     workers = 'worker_processes ' + str(args.max_threads) + ';'
-    subprocess.check_call('echo "upstream mono {\n' + ';\n'.join('\tserver 127.0.0.1:' + str(port) for port in range(9001, 9001 + args.max_threads)) + ';\n}" > ' + root + '/nginx.upstream.conf', shell=True);
-    subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c ' + root + '/nginx.conf -g "' + workers + '"', shell=True)
+    subprocess.check_call('echo "upstream mono {\n' + ';\n'.join('\tserver 127.0.0.1:' + str(port) for port in range(9001, 9001 + args.max_threads)) + ';\n}" > ' + root + '/nginx.upstream.conf', shell=True, stderr=errfile, stdout=logfile);
+    subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c ' + root + '/nginx.conf -g "' + workers + '"', shell=True, stderr=errfile, stdout=logfile)
     
     
     # fastcgi
     # fastcgi
     for port in range(9001, 9001 + args.max_threads):
     for port in range(9001, 9001 + args.max_threads):
-      subprocess.Popen("MONO_OPTIONS=--gc=sgen fastcgi-mono-server4 /applications=/:. /socket=tcp:127.0.0.1:" + str(port) + " &", shell=True, cwd=app)
+      subprocess.Popen("MONO_OPTIONS=--gc=sgen fastcgi-mono-server4 /applications=/:. /socket=tcp:127.0.0.1:" + str(port) + " &", shell=True, cwd=app, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
     return 0
     return 0
   
   
-  subprocess.check_call("sudo /usr/local/nginx/sbin/nginx -c " + root + "/nginx.conf -s stop", shell=True)
-  subprocess.check_call("rm -f " + root + "/nginx.upstream.conf", shell=True)
+  subprocess.check_call("sudo /usr/local/nginx/sbin/nginx -c " + root + "/nginx.conf -s stop", shell=True, stderr=errfile, stdout=logfile)
+  subprocess.check_call("rm -f " + root + "/nginx.upstream.conf", shell=True, stderr=errfile, stdout=logfile)
   #
   #
   # stop mono
   # stop mono
   #
   #

+ 6 - 6
aspnet/setup_xsp.py

@@ -3,22 +3,22 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
     return 1
     return 1
   
   
   setup_util.replace_text("aspnet/src/Web.config", "localhost", args.database_host)
   setup_util.replace_text("aspnet/src/Web.config", "localhost", args.database_host)
 
 
   try:
   try:
-    subprocess.check_call("rm -rf bin obj", shell=True, cwd="aspnet/src")
-    subprocess.check_call("xbuild /p:Configuration=Release", shell=True, cwd="aspnet/src")
-    subprocess.check_call("sudo chown -R ubuntu:ubuntu /usr/local/etc/mono", shell=True)
-    subprocess.Popen("MONO_OPTIONS=--gc=sgen xsp4 --nonstop", shell=True, cwd="aspnet/src")
+    subprocess.check_call("rm -rf bin obj", shell=True, cwd="aspnet/src", stderr=errfile, stdout=logfile)
+    subprocess.check_call("xbuild /p:Configuration=Release", shell=True, cwd="aspnet/src", stderr=errfile, stdout=logfile)
+    subprocess.check_call("sudo chown -R ubuntu:ubuntu /usr/local/etc/mono", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.Popen("MONO_OPTIONS=--gc=sgen xsp4 --nonstop", shell=True, cwd="aspnet/src", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
     return 0
     return 0
   
   

+ 8 - 8
beego/setup.py

@@ -3,18 +3,18 @@ import subprocess
 import sys
 import sys
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\beego&&go get ./...", shell=True, cwd="beego")
-    subprocess.Popen("setup.bat", shell=True, cwd="beego")
+    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\beego&&go get ./...", shell=True, cwd="beego", stderr=errfile, stdout=logfile)
+    subprocess.Popen("setup.bat", shell=True, cwd="beego", stderr=errfile, stdout=logfile)
     return 0
     return 0
-  subprocess.call("go get ./...", shell=True, cwd="beego")
-  subprocess.Popen("go run src/hello/hello.go".rsplit(" "), cwd="beego")
+  subprocess.call("go get ./...", shell=True, cwd="beego", stderr=errfile, stdout=logfile)
+  subprocess.Popen("go run src/hello/hello.go".rsplit(" "), cwd="beego", stderr=errfile, stdout=logfile)
   return 0
   return 0
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.call("taskkill /f /im go.exe > NUL", shell=True)
-    subprocess.call("taskkill /f /im hello.exe > NUL", shell=True)
+    subprocess.call("taskkill /f /im go.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call("taskkill /f /im hello.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   out, err = p.communicate()
   out, err = p.communicate()

+ 1 - 1
beego/src/hello/hello.go

@@ -2,7 +2,7 @@ package main
 
 
 import (
 import (
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego"
-	"runtime"
+	//"runtime"
 )
 )
 
 
 type MessageStruct struct {
 type MessageStruct struct {

+ 31 - 0
benchmark.cfg.example

@@ -0,0 +1,31 @@
+[Defaults]
+# Available Keys: 
+# client_host='localhost'
+# client_identity_file=None
+# client_user=None
+# database_host=None
+# database_identity_file=None
+# database_os='linux'
+# database_user=None
+# duration=60
+# exclude=None
+# install='all'
+# install_error_action='continue'
+# install_software=False
+# list_test_metadata=False
+# list_tests=False
+# max_concurrency=256
+# max_queries=20
+# max_threads=8
+# mode='benchmark'
+# name='ec2'
+# os='linux'
+# parse=None
+# password_prompt=False
+# query_interval=5
+# server_host='localhost'
+# sleep=60
+# starting_concurrency=8
+# test=None
+# type='all'
+# verbose=True

+ 3 - 3
bottle/setup.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     proc = subprocess.Popen([
     proc = subprocess.Popen([
@@ -19,10 +19,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="bottle")
+        cwd="bottle", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 6 - 6
bottle/setup_nginxuwsgi.py

@@ -7,21 +7,21 @@ bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
 config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
 config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
 NCPU = multiprocessing.cpu_count()
 NCPU = multiprocessing.cpu_count()
 
 
-def start(args):
+def start(args, logfile, errfile):
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     try:
     try:
         subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c ' +
         subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c ' +
-            config_dir + '/nginx_uwsgi.conf', shell=True)
+            config_dir + '/nginx_uwsgi.conf', shell=True, stderr=errfile, stdout=logfile)
         # Run in the background, but keep stdout/stderr for easy debugging
         # Run in the background, but keep stdout/stderr for easy debugging
         subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
         subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
             ' --processes ' + str(NCPU * 3) +
             ' --processes ' + str(NCPU * 3) +
             ' --wsgi app:app',
             ' --wsgi app:app',
-            shell=True, cwd='bottle')
+            shell=True, cwd='bottle', stderr=errfile, stdout=logfile)
         return 0
         return 0
     except subprocess.CalledProcessError:
     except subprocess.CalledProcessError:
         return 1
         return 1
 
 
-def stop():
-    subprocess.call('sudo /usr/local/nginx/sbin/nginx -s stop', shell=True)
-    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True)
+def stop(logfile, errfile):
+    subprocess.call('sudo /usr/local/nginx/sbin/nginx -s stop', shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0

+ 3 - 3
bottle/setup_py3.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     proc = subprocess.Popen([
     proc = subprocess.Popen([
@@ -19,10 +19,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="bottle")
+        cwd="bottle", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 3 - 3
bottle/setup_pypy.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
     proc = subprocess.Popen([
     proc = subprocess.Popen([
@@ -19,10 +19,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="bottle")
+        cwd="bottle", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 11 - 11
cake/setup.py

@@ -7,7 +7,7 @@ from os.path import expanduser
 
 
 home = expanduser("~")
 home = expanduser("~")
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("cake/app/Config/database.php", "'host' => '.*',", "'host' => '" + args.database_host + "',")
   setup_util.replace_text("cake/app/Config/database.php", "'host' => '.*',", "'host' => '" + args.database_host + "',")
   setup_util.replace_text("cake/deploy/cake", "\".*\/FrameworkBenchmarks", "\"" + home + "/FrameworkBenchmarks")
   setup_util.replace_text("cake/deploy/cake", "\".*\/FrameworkBenchmarks", "\"" + home + "/FrameworkBenchmarks")
   setup_util.replace_text("cake/deploy/cake", "Directory .*\/FrameworkBenchmarks", "Directory " + home + "/FrameworkBenchmarks")
   setup_util.replace_text("cake/deploy/cake", "Directory .*\/FrameworkBenchmarks", "Directory " + home + "/FrameworkBenchmarks")
@@ -16,28 +16,28 @@ def start(args):
   try:
   try:
     if os.name == 'nt':
     if os.name == 'nt':
       setup_util.replace_text("cake/app/Config/core.php", "'Apc'", "'Wincache'")
       setup_util.replace_text("cake/app/Config/core.php", "'Apc'", "'Wincache'")
-      subprocess.check_call('icacls "C:\\FrameworkBenchmarks\\cake" /grant "IIS_IUSRS:(OI)(CI)F"', shell=True)
-      subprocess.check_call('appcmd add site /name:PHP /bindings:http/*:8080: /physicalPath:"C:\\FrameworkBenchmarks\\cake\\app\\webroot"', shell=True)
+      subprocess.check_call('icacls "C:\\FrameworkBenchmarks\\cake" /grant "IIS_IUSRS:(OI)(CI)F"', shell=True, stderr=errfile, stdout=logfile)
+      subprocess.check_call('appcmd add site /name:PHP /bindings:http/*:8080: /physicalPath:"C:\\FrameworkBenchmarks\\cake\\app\\webroot"', shell=True, stderr=errfile, stdout=logfile)
       return 0
       return 0
     #subprocess.check_call("sudo cp cake/deploy/cake /etc/apache2/sites-available/", shell=True)
     #subprocess.check_call("sudo cp cake/deploy/cake /etc/apache2/sites-available/", shell=True)
     #subprocess.check_call("sudo a2ensite cake", shell=True)
     #subprocess.check_call("sudo a2ensite cake", shell=True)
-    subprocess.check_call("sudo chown -R www-data:www-data cake", shell=True)
+    subprocess.check_call("sudo chown -R www-data:www-data cake", shell=True, stderr=errfile, stdout=logfile)
     #subprocess.check_call("sudo /etc/init.d/apache2 start", shell=True)
     #subprocess.check_call("sudo /etc/init.d/apache2 start", shell=True)
-    subprocess.check_call("sudo php-fpm --fpm-config config/php-fpm.conf -g " + home + "/FrameworkBenchmarks/cake/deploy/php-fpm.pid", shell=True)
-    subprocess.check_call("sudo /usr/local/nginx/sbin/nginx -c " + home + "/FrameworkBenchmarks/cake/deploy/nginx.conf", shell=True)
+    subprocess.check_call("sudo php-fpm --fpm-config config/php-fpm.conf -g " + home + "/FrameworkBenchmarks/cake/deploy/php-fpm.pid", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.check_call("sudo /usr/local/nginx/sbin/nginx -c " + home + "/FrameworkBenchmarks/cake/deploy/nginx.conf", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
-def stop():
+def stop(logfile, errfile):
   try:
   try:
     if os.name == 'nt':
     if os.name == 'nt':
-      subprocess.call('appcmd delete site PHP', shell=True)
+      subprocess.call('appcmd delete site PHP', shell=True, stderr=errfile, stdout=logfile)
       return 0
       return 0
-    subprocess.call("sudo /usr/local/nginx/sbin/nginx -s stop", shell=True)
-    subprocess.call("sudo kill -QUIT $( cat cake/deploy/php-fpm.pid )", shell=True)
+    subprocess.call("sudo /usr/local/nginx/sbin/nginx -s stop", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call("sudo kill -QUIT $( cat cake/deploy/php-fpm.pid )", shell=True, stderr=errfile, stdout=logfile)
     #subprocess.check_call("sudo a2dissite cake", shell=True)
     #subprocess.check_call("sudo a2dissite cake", shell=True)
     #subprocess.check_call("sudo /etc/init.d/apache2 stop", shell=True)
     #subprocess.check_call("sudo /etc/init.d/apache2 stop", shell=True)
-    subprocess.check_call("sudo chown -R $USER:$USER cake", shell=True)
+    subprocess.check_call("sudo chown -R $USER:$USER cake", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1

+ 7 - 7
compojure/setup.py

@@ -3,20 +3,20 @@ import subprocess
 import sys
 import sys
 import setup_util
 import setup_util
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("compojure/hello/src/hello/handler.clj", ":subname \"//.*:3306", ":subname \"//" + args.database_host + ":3306")
   setup_util.replace_text("compojure/hello/src/hello/handler.clj", ":subname \"//.*:3306", ":subname \"//" + args.database_host + ":3306")
 
 
   try:
   try:
-    subprocess.check_call("lein ring uberwar", shell=True, cwd="compojure/hello")
-    subprocess.check_call("rm -rf $RESIN_HOME/webapps/*", shell=True)
-    subprocess.check_call("cp compojure/hello/target/hello-compojure-standalone.war $RESIN_HOME/webapps/compojure.war", shell=True)
-    subprocess.check_call("$RESIN_HOME/bin/resinctl start", shell=True)
+    subprocess.check_call("lein ring uberwar", shell=True, cwd="compojure/hello", stderr=errfile, stdout=logfile)
+    subprocess.check_call("rm -rf $RESIN_HOME/webapps/*", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.check_call("cp compojure/hello/target/hello-compojure-standalone.war $RESIN_HOME/webapps/compojure.war", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.check_call("$RESIN_HOME/bin/resinctl start", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
-def stop():
+def stop(logfile, errfile):
   try:
   try:
-    subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True)
+    subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1

+ 2 - 2
config/benchmark_profile

@@ -1,7 +1,7 @@
 export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64
 export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64
 export RESIN_HOME=~/FrameworkBenchmarks/installs/resin-4.0.36
 export RESIN_HOME=~/FrameworkBenchmarks/installs/resin-4.0.36
-export GRAILS_HOME=~/FrameworkBenchmarks/installs/grails-2.3.1
-export VERTX_HOME=~/FrameworkBenchmarks/installs/vert.x-2.0.2-final
+export GRAILS_HOME=~/FrameworkBenchmarks/installs/grails-2.3.3
+export VERTX_HOME=~/FrameworkBenchmarks/installs/vert.x-2.1M1
 export GOROOT=~/FrameworkBenchmarks/installs/go
 export GOROOT=~/FrameworkBenchmarks/installs/go
 export GOPATH=~/FrameworkBenchmarks/go:~/FrameworkBenchmarks/webgo:~/FrameworkBenchmarks/revel
 export GOPATH=~/FrameworkBenchmarks/go:~/FrameworkBenchmarks/webgo:~/FrameworkBenchmarks/revel
 export TOMCAT_HOME=~/FrameworkBenchmarks/installs/apache-tomcat-7.0.35
 export TOMCAT_HOME=~/FrameworkBenchmarks/installs/apache-tomcat-7.0.35

+ 28 - 0
config/create-postgres-urweb.sql

@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS uw_Bench_world;
+
+CREATE TABLE uw_Bench_world AS
+SELECT uw_id::int8, trunc(random()*9999+1)::int8 AS uw_randomnumber
+FROM generate_series(1,10000) AS uw_id;
+
+ALTER TABLE uw_Bench_world ADD PRIMARY KEY (uw_id);
+ALTER TABLE uw_Bench_world ALTER COLUMN uw_randomnumber SET NOT NULL;
+
+DROP TABLE IF EXISTS uw_Bench_fortune;
+CREATE TABLE uw_Bench_fortune (
+  uw_id int8 NOT NULL,
+  uw_message text NOT NULL,
+  PRIMARY KEY (uw_id)
+);
+
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.")</script>');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (7, 'Any program that runs right is obsolete.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (9, 'Feature: A bug with seniority.');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (1, 'fortune: No such file or directory');
+INSERT INTO uw_Bench_fortune (uw_id, uw_message) VALUES (12, 'フレームワークのベンチマーク');

+ 15 - 15
config/create.js

@@ -1,25 +1,25 @@
 use hello_world
 use hello_world
 db.world.drop()
 db.world.drop()
 for (var i = 1; i <= 10000; i++) {
 for (var i = 1; i <= 10000; i++) {
-  db.world.save( { id: i, randomNumber: (Math.floor(Math.random() * 10000) + 1) })
+  db.world.save( { _id: i, randomNumber: (Math.floor(Math.random() * 10000) + 1) })
 }
 }
 
 
 // http://docs.mongodb.org/manual/applications/optimization/
 // http://docs.mongodb.org/manual/applications/optimization/
-db.world.ensureIndex({id: 1})
+db.world.ensureIndex({_id: 1})
 
 
 db.fortune.drop()
 db.fortune.drop()
 
 
-db.fortune.save( {id: 1, message: 'fortune: No such file or directory'} );
-db.fortune.save( {id: 2, message: "A computer scientist is someone who fixes things that aren't broken."} );
-db.fortune.save( {id: 3, message: 'After enough decimal places, nobody gives a damn.'} );
-db.fortune.save( {id: 4, message: 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1'} );
-db.fortune.save( {id: 5, message: 'A computer program does what you tell it to do, not what you want it to do.'} );
-db.fortune.save( {id: 6, message: 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen'} );
-db.fortune.save( {id: 7, message: 'Any program that runs right is obsolete.'} );
-db.fortune.save( {id: 8, message: 'A list is only as strong as its weakest link. — Donald Knuth'} );
-db.fortune.save( {id: 9, message: 'Feature: A bug with seniority.'} );
-db.fortune.save( {id: 10, message: 'Computers make very fast, very accurate mistakes.'} );
-db.fortune.save( {id: 11, message: '<script>alert("This should not be displayed in a browser alert box.");</script>'} );
-db.fortune.save( {id: 12, message: 'フレームワークのベンチマーク'} );
+db.fortune.save( {_id: 1, message: 'fortune: No such file or directory'} );
+db.fortune.save( {_id: 2, message: "A computer scientist is someone who fixes things that aren't broken."} );
+db.fortune.save( {_id: 3, message: 'After enough decimal places, nobody gives a damn.'} );
+db.fortune.save( {_id: 4, message: 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1'} );
+db.fortune.save( {_id: 5, message: 'A computer program does what you tell it to do, not what you want it to do.'} );
+db.fortune.save( {_id: 6, message: 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen'} );
+db.fortune.save( {_id: 7, message: 'Any program that runs right is obsolete.'} );
+db.fortune.save( {_id: 8, message: 'A list is only as strong as its weakest link. — Donald Knuth'} );
+db.fortune.save( {_id: 9, message: 'Feature: A bug with seniority.'} );
+db.fortune.save( {_id: 10, message: 'Computers make very fast, very accurate mistakes.'} );
+db.fortune.save( {_id: 11, message: '<script>alert("This should not be displayed in a browser alert box.");</script>'} );
+db.fortune.save( {_id: 12, message: 'フレームワークのベンチマーク'} );
 
 
-db.fortune.ensureIndex({id: 1})
+db.fortune.ensureIndex({_id: 1})

+ 5 - 5
cowboy/setup_erlang.py

@@ -2,17 +2,17 @@ import subprocess
 import sys
 import sys
 import setup_util
 import setup_util
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("cowboy/src/hello_world_app.erl", "\"benchmarkdbpass\", \".*\", 3306", "\"benchmarkdbpass\", \"" + args.database_host + "\", 3306")
   setup_util.replace_text("cowboy/src/hello_world_app.erl", "\"benchmarkdbpass\", \".*\", 3306", "\"benchmarkdbpass\", \"" + args.database_host + "\", 3306")
 
 
   try:
   try:
-    subprocess.check_call("./rebar get-deps", shell=True, cwd="cowboy")
-    subprocess.check_call("./rebar compile", shell=True, cwd="cowboy")
-    subprocess.check_call("erl -pa ebin deps/*/ebin +sbwt very_long +swt very_low -s hello_world -noshell -detached", shell=True, cwd="cowboy")
+    subprocess.check_call("./rebar get-deps", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)
+    subprocess.check_call("./rebar compile", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)
+    subprocess.check_call("erl -pa ebin deps/*/ebin +sbwt very_long +swt very_low -s hello_world -noshell -detached", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
-def stop():
+def stop(logfile, errfile):
   try:
   try:
     subprocess.check_call("killall beam.smp", shell=True, cwd="/usr/bin")
     subprocess.check_call("killall beam.smp", shell=True, cwd="/usr/bin")
     return 0
     return 0

+ 4 - 4
cpoll_cppsp/setup.py

@@ -3,13 +3,13 @@ import sys
 import os
 import os
 import setup_util 
 import setup_util 
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("cpoll_cppsp/www/connectioninfo.H", "\\#define BENCHMARK_DB_HOST \".*\"", "#define BENCHMARK_DB_HOST \"" + args.database_host + "\"")
   setup_util.replace_text("cpoll_cppsp/www/connectioninfo.H", "\\#define BENCHMARK_DB_HOST \".*\"", "#define BENCHMARK_DB_HOST \"" + args.database_host + "\"")
-  subprocess.check_call("make", shell=True, cwd="cpoll_cppsp")
-  subprocess.Popen("./run_application \"$(pwd)\"/www -g g++-4.8 -m /forcedynamic.cppsm", shell=True, cwd="cpoll_cppsp");
+  subprocess.check_call("make", shell=True, cwd="cpoll_cppsp", stderr=errfile, stdout=logfile)
+  subprocess.Popen("./run_application \"$(pwd)\"/www -g g++-4.8 -m /forcedynamic.cppsm", shell=True, cwd="cpoll_cppsp", stderr=errfile, stdout=logfile);
   return 0
   return 0
 
 
-def stop():
+def stop(logfile, errfile):
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   out, err = p.communicate()
   out, err = p.communicate()
   for line in out.splitlines():
   for line in out.splitlines():

+ 4 - 4
dancer/setup.py

@@ -7,18 +7,18 @@ import getpass
 
 
 home = expanduser("~")
 home = expanduser("~")
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("dancer/app.pl", "localhost", ""+ args.database_host +"")
   setup_util.replace_text("dancer/app.pl", "localhost", ""+ args.database_host +"")
   setup_util.replace_text("dancer/nginx.conf", "USR", getpass.getuser())
   setup_util.replace_text("dancer/nginx.conf", "USR", getpass.getuser())
   setup_util.replace_text("dancer/nginx.conf", "server unix:.*\/FrameworkBenchmarks", "server unix:" + home + "/FrameworkBenchmarks")
   setup_util.replace_text("dancer/nginx.conf", "server unix:.*\/FrameworkBenchmarks", "server unix:" + home + "/FrameworkBenchmarks")
 
 
   try:
   try:
-    subprocess.Popen("plackup -E production -s Starman --workers=" + str(args.max_threads) + " -l " + home + "/FrameworkBenchmarks/dancer/frameworks-benchmark.sock -a ./app.pl", shell=True, cwd="dancer")
-    subprocess.check_call("sudo /usr/local/nginx/sbin/nginx -c " + home + "/FrameworkBenchmarks/dancer/nginx.conf", shell=True)
+    subprocess.Popen("plackup -E production -s Starman --workers=" + str(args.max_threads) + " -l " + home + "/FrameworkBenchmarks/dancer/frameworks-benchmark.sock -a ./app.pl", shell=True, cwd="dancer", stderr=errfile, stdout=logfile)
+    subprocess.check_call("sudo /usr/local/nginx/sbin/nginx -c " + home + "/FrameworkBenchmarks/dancer/nginx.conf", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
-def stop():
+def stop(logfile, errfile):
   try:
   try:
     subprocess.call("sudo /usr/local/nginx/sbin/nginx -s stop", shell=True)
     subprocess.call("sudo /usr/local/nginx/sbin/nginx -s stop", shell=True)
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)

+ 6 - 6
dart-start/setup.py

@@ -3,19 +3,19 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text('dart-start/postgresql.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart-start/postgresql.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart-start/mongodb.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart-start/mongodb.yaml', 'host: .*', 'host: ' + args.database_host)
   try:
   try:
     #
     #
     # install dart dependencies
     # install dart dependencies
     #
     #
-    subprocess.check_call('pub install', shell=True, cwd='dart-start')
+    subprocess.check_call('pub install', shell=True, cwd='dart-start', stderr=errfile, stdout=logfile)
     #
     #
     # start dart servers
     # start dart servers
     #
     #
     for port in range(9001, 9001 + args.max_threads):
     for port in range(9001, 9001 + args.max_threads):
-      subprocess.Popen('dart server.dart -a 127.0.0.1 -p ' + str(port) + ' -d ' + str(args.max_concurrency / args.max_threads), shell=True, cwd='dart-start')
+      subprocess.Popen('dart server.dart -a 127.0.0.1 -p ' + str(port) + ' -d ' + str(args.max_concurrency / args.max_threads), shell=True, cwd='dart-start', stderr=errfile, stdout=logfile)
     #
     #
     # create nginx configuration
     # create nginx configuration
     #
     #
@@ -52,16 +52,16 @@ def start(args):
     #
     #
     # start nginx
     # start nginx
     #
     #
-    subprocess.Popen('sudo /usr/sbin/nginx -c `pwd`/nginx.conf', shell=True, cwd='dart-start');
+    subprocess.Popen('sudo /usr/sbin/nginx -c `pwd`/nginx.conf', shell=True, cwd='dart-start', stderr=errfile, stdout=logfile);
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   #
   #
   # stop nginx
   # stop nginx
   #
   #
-  subprocess.check_call('sudo /usr/sbin/nginx -c `pwd`/nginx.conf -s stop', shell=True, cwd='dart-start')
+  subprocess.check_call('sudo /usr/sbin/nginx -c `pwd`/nginx.conf -s stop', shell=True, cwd='dart-start', stderr=errfile, stdout=logfile)
   os.remove('dart-start/nginx.conf')
   os.remove('dart-start/nginx.conf')
   #
   #
   # stop dart servers
   # stop dart servers

+ 10 - 10
dart-stream/server.dart

@@ -81,7 +81,7 @@ class World {
 
 
   World(this.id, this.randomnumber);
   World(this.id, this.randomnumber);
 
 
-  toJson() => { "id": id, "randomnumber": randomnumber };
+  toJson() => { "id": id, "randomNumber": randomnumber };
 }
 }
 
 
 main() {
 main() {
@@ -188,8 +188,8 @@ _dbMongoTest(HttpConnect connect) {
   
   
   return _mongoQuery().then((data) {
   return _mongoQuery().then((data) {
     connect.response.write(json.stringify({
     connect.response.write(json.stringify({
-      "id": data["id"],
-      "randomnumber": data["randomnumber"]
+      "id": data["_id"],
+      "randomNumber": data["randomNumber"]
     }));
     }));
   });
   });
 }
 }
@@ -207,8 +207,8 @@ _queriesMongoTest(HttpConnect connect) {
     .then((response) {
     .then((response) {
       var results = response.map((world) {
       var results = response.map((world) {
         return {
         return {
-          "id": world["id"],
-          "randomnumber": world["randomnumber"]
+          "id": world["_id"],
+          "randomNumber": world["randomNumber"]
         };
         };
       });
       });
       connect.response.write(json.stringify(results.toList()));
       connect.response.write(json.stringify(results.toList()));
@@ -221,7 +221,7 @@ _updatesMongoTest(HttpConnect connect) {
   return Future.wait(new List.generate(queries, (index) {
   return Future.wait(new List.generate(queries, (index) {
       return _mongoQuery()
       return _mongoQuery()
           .then((world) {
           .then((world) {
-            world["randomnumber"] = _RANDOM.nextInt(_WORLD_TABLE_SIZE);
+            world["randomNumber"] = _RANDOM.nextInt(_WORLD_TABLE_SIZE);
             return _worldCollection.update( { "_id": world["_id"] }, world)
             return _worldCollection.update( { "_id": world["_id"] }, world)
                 .then((_) => world);
                 .then((_) => world);
           });
           });
@@ -229,8 +229,8 @@ _updatesMongoTest(HttpConnect connect) {
     .then((worlds) {
     .then((worlds) {
       var result = worlds.map((world) {
       var result = worlds.map((world) {
         return {
         return {
-          "id": world["id"],
-          "randomnumber": world["randomnumber"]
+          "id": world["_id"],
+          "randomNumber": world["randomNumber"]
         };
         };
       });
       });
       connect.response.write(json.stringify(result.toList()));
       connect.response.write(json.stringify(result.toList()));
@@ -241,7 +241,7 @@ _fortunesMongoTest(HttpConnect connect) {
   
   
   return _fortuneCollection.find().toList().then((fortunes) {
   return _fortuneCollection.find().toList().then((fortunes) {
     fortunes = fortunes.map((fortune) {
     fortunes = fortunes.map((fortune) {
-      return new Fortune(fortune["id"], fortune["message"]);
+      return new Fortune(fortune["_id"], fortune["message"]);
     }).toList();
     }).toList();
     fortunes.add(new Fortune(0, 'Additional fortune added at request time.'));
     fortunes.add(new Fortune(0, 'Additional fortune added at request time.'));
     fortunes.sort();
     fortunes.sort();
@@ -301,6 +301,6 @@ _query() {
 // runs a mongo query and returns a promise
 // runs a mongo query and returns a promise
 _mongoQuery() {
 _mongoQuery() {
   return _worldCollection.findOne({
   return _worldCollection.findOne({
-    "id": _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1
+    "_id": _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1
   });
   });
 }
 }

+ 6 - 6
dart-stream/setup.py

@@ -3,19 +3,19 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text('dart-stream/postgresql.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart-stream/postgresql.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart-stream/mongodb.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart-stream/mongodb.yaml', 'host: .*', 'host: ' + args.database_host)
   try:
   try:
     #
     #
     # install dart dependencies
     # install dart dependencies
     #
     #
-    subprocess.check_call('pub install', shell=True, cwd='dart-stream')
+    subprocess.check_call('pub install', shell=True, cwd='dart-stream', stderr=errfile, stdout=logfile)
     #
     #
     # start dart servers
     # start dart servers
     #
     #
     for port in range(9001, 9001 + args.max_threads):
     for port in range(9001, 9001 + args.max_threads):
-      subprocess.Popen('dart server.dart -a 127.0.0.1 -p ' + str(port) + ' -d ' + str(args.max_concurrency / args.max_threads), shell=True, cwd='dart-stream')
+      subprocess.Popen('dart server.dart -a 127.0.0.1 -p ' + str(port) + ' -d ' + str(args.max_concurrency / args.max_threads), shell=True, cwd='dart-stream', stderr=errfile, stdout=logfile)
     #
     #
     # create nginx configuration
     # create nginx configuration
     #
     #
@@ -52,16 +52,16 @@ def start(args):
     #
     #
     # start nginx
     # start nginx
     #
     #
-    subprocess.Popen('sudo /usr/sbin/nginx -c `pwd`/nginx.conf', shell=True, cwd='dart-stream');
+    subprocess.Popen('sudo /usr/sbin/nginx -c `pwd`/nginx.conf', shell=True, cwd='dart-stream', stderr=errfile, stdout=logfile);
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   #
   #
   # stop nginx
   # stop nginx
   #
   #
-  subprocess.check_call('sudo /usr/sbin/nginx -c `pwd`/nginx.conf -s stop', shell=True, cwd='dart-stream')
+  subprocess.check_call('sudo /usr/sbin/nginx -c `pwd`/nginx.conf -s stop', shell=True, cwd='dart-stream', stderr=errfile, stdout=logfile)
   os.remove('dart-stream/nginx.conf')
   os.remove('dart-stream/nginx.conf')
   #
   #
   # stop dart servers
   # stop dart servers

+ 5 - 5
dart/pubspec.yaml

@@ -1,8 +1,8 @@
 name: dartbenchmark
 name: dartbenchmark
 description: A benchmark of dart
 description: A benchmark of dart
 dependencies:
 dependencies:
-  args: 0.5.9
-  crypto: 0.5.13
-  mustache: 0.1.5
-  postgresql: 0.2.7
-  yaml: 0.5.7
+  args: 0.9.0
+  crypto: 0.9.0
+  mustache: 0.1.6
+  postgresql: 0.2.12
+  yaml: 0.9.0

+ 5 - 11
dart/server.dart

@@ -1,7 +1,6 @@
 import 'dart:async' show Future;
 import 'dart:async' show Future;
 import 'dart:io';
 import 'dart:io';
-import 'dart:utf';
-import 'dart:json' as json;
+import 'dart:convert';
 import 'dart:math' show Random;
 import 'dart:math' show Random;
 import 'package:args/args.dart' show ArgParser;
 import 'package:args/args.dart' show ArgParser;
 import 'package:mustache/mustache.dart' as mustache;
 import 'package:mustache/mustache.dart' as mustache;
@@ -13,12 +12,12 @@ import 'package:yaml/yaml.dart' as yaml;
 /// address and port for incoming connections is configurable via command line
 /// address and port for incoming connections is configurable via command line
 /// arguments, as is the number of database connections to be maintained in the
 /// arguments, as is the number of database connections to be maintained in the
 /// connection pool.
 /// connection pool.
-main() {
+main(args) {
   var parser = new ArgParser();
   var parser = new ArgParser();
   parser.addOption('address', abbr: 'a', defaultsTo: '0.0.0.0');
   parser.addOption('address', abbr: 'a', defaultsTo: '0.0.0.0');
   parser.addOption('port', abbr: 'p', defaultsTo: '8080');
   parser.addOption('port', abbr: 'p', defaultsTo: '8080');
   parser.addOption('dbconnections', abbr: 'd', defaultsTo: '256');
   parser.addOption('dbconnections', abbr: 'd', defaultsTo: '256');
-  var arguments = parser.parse(new Options().arguments);
+  var arguments = parser.parse(args);
   _startServer(
   _startServer(
       arguments['address'],
       arguments['address'],
       int.parse(arguments['port']),
       int.parse(arguments['port']),
@@ -126,16 +125,11 @@ _parseInt(text) =>
 _sendResponse(request, statusCode, [ type, response ]) {
 _sendResponse(request, statusCode, [ type, response ]) {
   request.response.statusCode = statusCode;
   request.response.statusCode = statusCode;
   request.response.headers.date = new DateTime.now();
   request.response.headers.date = new DateTime.now();
-  //
-  // Prevent GZIP encoding, because it is disallowed in the rules for these
-  // benchmark tests.
-  //
-  request.response.headers.add(HttpHeaders.CONTENT_ENCODING, '');
   if (type != null) {
   if (type != null) {
     request.response.headers.contentType = type;
     request.response.headers.contentType = type;
   }
   }
   if (response != null) {
   if (response != null) {
-    var data = encodeUtf8(response);
+    var data = UTF8.encode(response);
     request.response.contentLength = data.length;
     request.response.contentLength = data.length;
     request.response.add(data);
     request.response.add(data);
   } else {
   } else {
@@ -151,7 +145,7 @@ _sendHtml(request, response) {
 
 
 /// Completes the given [request] by writing the [response] as JSON.
 /// Completes the given [request] by writing the [response] as JSON.
 _sendJson(request, response) {
 _sendJson(request, response) {
-  _sendResponse(request, HttpStatus.OK, _TYPE_JSON, json.stringify(response));
+  _sendResponse(request, HttpStatus.OK, _TYPE_JSON, JSON.encode(response));
 }
 }
 
 
 /// Completes the given [request] by writing the [response] as plain text.
 /// Completes the given [request] by writing the [response] as plain text.

+ 6 - 6
dart/setup.py

@@ -3,18 +3,18 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text('dart/postgresql.yaml', 'host: .*', 'host: ' + args.database_host)
   setup_util.replace_text('dart/postgresql.yaml', 'host: .*', 'host: ' + args.database_host)
   try:
   try:
     #
     #
     # install dart dependencies
     # install dart dependencies
     #
     #
-    subprocess.check_call('pub install', shell=True, cwd='dart')
+    subprocess.check_call('pub install', shell=True, cwd='dart', stderr=errfile, stdout=logfile)
     #
     #
     # start dart servers
     # start dart servers
     #
     #
     for port in range(9001, 9001 + args.max_threads):
     for port in range(9001, 9001 + args.max_threads):
-      subprocess.Popen('dart server.dart -a 127.0.0.1 -p ' + str(port) + ' -d ' + str(args.max_concurrency / args.max_threads), shell=True, cwd='dart')
+      subprocess.Popen('dart server.dart -a 127.0.0.1 -p ' + str(port) + ' -d ' + str(args.max_concurrency / args.max_threads), shell=True, cwd='dart', stderr=errfile, stdout=logfile)
     #
     #
     # create nginx configuration
     # create nginx configuration
     #
     #
@@ -51,16 +51,16 @@ def start(args):
     #
     #
     # start nginx
     # start nginx
     #
     #
-    subprocess.Popen('sudo /usr/local/nginx/sbin/nginx -c `pwd`/nginx.conf', shell=True, cwd='dart');
+    subprocess.Popen('sudo /usr/local/nginx/sbin/nginx -c `pwd`/nginx.conf', shell=True, cwd='dart', stderr=errfile, stdout=logfile);
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def stop():
+def stop(logfile, errfile):
   #
   #
   # stop nginx
   # stop nginx
   #
   #
-  subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c `pwd`/nginx.conf -s stop', shell=True, cwd='dart')
+  subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c `pwd`/nginx.conf -s stop', shell=True, cwd='dart', stderr=errfile, stdout=logfile)
   os.remove('dart/nginx.conf')
   os.remove('dart/nginx.conf')
   #
   #
   # stop dart servers
   # stop dart servers

+ 3 - 3
django/setup.py

@@ -10,7 +10,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
     setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
     setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
     setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
@@ -24,10 +24,10 @@ def start(args):
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
         cwd="django/hello",
         cwd="django/hello",
-        env=env)
+        env=env, stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 3 - 3
django/setup_pg.py

@@ -10,7 +10,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
     setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
     setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
     setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
@@ -24,10 +24,10 @@ def start(args):
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
         cwd="django/hello",
         cwd="django/hello",
-        env=env)
+        env=env, stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 3 - 3
django/setup_py3.py

@@ -10,7 +10,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
     setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
     setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
     setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
@@ -21,10 +21,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="django/hello")
+        cwd="django/hello", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 4 - 4
dropwizard/setup.py

@@ -6,16 +6,16 @@ import os
 
 
 home = expanduser("~")
 home = expanduser("~")
 
 
-def start(args):
+def start(args, logfile, errfile):
     setup_util.replace_text("dropwizard/hello-world.yml", "url: jdbc:mysql://.*/hello_world", "url: jdbc:mysql://" + args.database_host + ":3306/hello_world")
     setup_util.replace_text("dropwizard/hello-world.yml", "url: jdbc:mysql://.*/hello_world", "url: jdbc:mysql://" + args.database_host + ":3306/hello_world")
 
 
     try:
     try:
-        subprocess.check_call("mvn clean package;", shell=True, cwd="dropwizard")
-        subprocess.Popen("java -jar target/hello-world-0.0.1-SNAPSHOT.jar server hello-world.yml", shell=True, cwd="dropwizard")
+        subprocess.check_call("mvn clean package;", shell=True, cwd="dropwizard", stderr=errfile, stdout=logfile)
+        subprocess.Popen("java -jar target/hello-world-0.0.1-SNAPSHOT.jar server hello-world.yml", shell=True, cwd="dropwizard", stderr=errfile, stdout=logfile)
         return 0
         return 0
     except subprocess.CalledProcessError:
     except subprocess.CalledProcessError:
         return 1
         return 1
-def stop():
+def stop(logfile, errfile):
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   out, err = p.communicate()
   out, err = p.communicate()
   for line in out.splitlines():
   for line in out.splitlines():

+ 6 - 6
elli/setup_erlang.py

@@ -2,20 +2,20 @@ import subprocess
 import sys
 import sys
 import setup_util
 import setup_util
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("elli/src/elli_bench_sup.erl", "\"benchmarkdbpass\", \".*\", 3306", "\"benchmarkdbpass\", \"" + args.database_host + "\", 3306")
   setup_util.replace_text("elli/src/elli_bench_sup.erl", "\"benchmarkdbpass\", \".*\", 3306", "\"benchmarkdbpass\", \"" + args.database_host + "\", 3306")
   
   
   try:
   try:
-    subprocess.check_call("./rebar get-deps", shell=True, cwd="elli")
-    subprocess.check_call("./rebar compile", shell=True, cwd="elli")
+    subprocess.check_call("./rebar get-deps", shell=True, cwd="elli", stderr=errfile, stdout=logfile)
+    subprocess.check_call("./rebar compile", shell=True, cwd="elli", stderr=errfile, stdout=logfile)
     # adding +K true seemed to actually slow performance
     # adding +K true seemed to actually slow performance
-    subprocess.check_call("erl -pa ebin deps/*/ebin +sbwt very_long +swt very_low -s elli_bench -noshell -detached", shell=True, cwd="elli")
+    subprocess.check_call("erl -pa ebin deps/*/ebin +sbwt very_long +swt very_low -s elli_bench -noshell -detached", shell=True, cwd="elli", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
-def stop():
+def stop(logfile, errfile):
   try:
   try:
-    subprocess.check_call("killall beam.smp", shell=True, cwd="/usr/bin")
+    subprocess.check_call("killall beam.smp", shell=True, cwd="/usr/bin", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1

+ 8 - 0
evhttp-sharp/.gitignore

@@ -0,0 +1,8 @@
+*.user
+*.suo
+*/bin/*
+*/obj/*
+obj/
+[Bb]in
+[Dd]ebug*/
+[Rr]elease*/

+ 24 - 0
evhttp-sharp/benchmark_config

@@ -0,0 +1,24 @@
+{
+  "framework": "evhttp-sharp",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/",
+      "plaintext_url": "/plaintext",
+      "port": 8085,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "None",
+      "framework": "evhttp-sharp",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "Mono",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "evhttp-sharp",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 28 - 0
evhttp-sharp/setup.py

@@ -0,0 +1,28 @@
+import subprocess
+import sys
+import setup_util
+import os
+
+root = os.getcwd() + "/evhttp-sharp"
+app = root + "/src"
+
+def start(args, logfile, errfile):
+  if os.name == 'nt':
+    return 1
+
+  try:
+    # build
+    subprocess.check_call("rm -rf bin obj", shell=True, cwd=app, stdout=logfile, stderr=errfile)
+    subprocess.check_call("xbuild /p:Configuration=Release", shell=True, cwd=app, stdout=logfile, stderr=errfile)
+    
+    subprocess.Popen("mono -O=all bin/Release/EvHttpSharpBenchmark.exe 127.0.0.1 8085 " + str(args.max_threads) + " &", shell=True, cwd=app, stdout=logfile, stderr=errfile)
+    return 0
+  except subprocess.CalledProcessError:
+    return 1
+
+def stop(logfile, errfile):
+  if os.name == 'nt':
+    return 0
+  
+  subprocess.check_call("pkill -9 mono", shell=True)
+  return 0

+ 69 - 0
evhttp-sharp/src/EvHttpSharpBenchmark.csproj

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{94B0D092-4377-4A5C-B222-4F005D316DB0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>EvHttpSharpBenchmark</RootNamespace>
+    <AssemblyName>EvHttpSharpBenchmark</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="EvHttpSharp">
+      <HintPath>lib\EvHttpSharp.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>lib\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="libevent_core-2-0-5.dll">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="libevent_extra-2-0-5.dll">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 20 - 0
evhttp-sharp/src/EvHttpSharpBenchmark.sln

@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvHttpSharpBenchmark", "EvHttpSharpBenchmark.csproj", "{94B0D092-4377-4A5C-B222-4F005D316DB0}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{94B0D092-4377-4A5C-B222-4F005D316DB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{94B0D092-4377-4A5C-B222-4F005D316DB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{94B0D092-4377-4A5C-B222-4F005D316DB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{94B0D092-4377-4A5C-B222-4F005D316DB0}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 37 - 0
evhttp-sharp/src/Program.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using EvHttpSharp;
+using Newtonsoft.Json;
+
+namespace EvHttpSharpBenchmark
+{
+	class Program
+	{
+		public static readonly JsonSerializer Serializer = new JsonSerializer();
+
+		static void Main (string[] args)
+		{
+			var host = new EventHttpListener(Handler);
+			host.Start(args[0], ushort.Parse(args[1]), int.Parse(args[2]));
+		}
+
+		private static void Handler(EventHttpRequest req)
+		{
+			var headers = new Dictionary<string, string>();
+			var resp = "Hello, World!";
+
+			if (!req.Uri.Contains("plaintext"))
+			{
+				var sw = new StringWriter();
+				Serializer.Serialize(sw, new {message = "Hello, world"});
+				resp = sw.ToString();
+				headers["Content-Type"] = "application/json";
+			}
+			req.Respond (HttpStatusCode.OK, headers, Encoding.UTF8.GetBytes (resp));
+		}
+	}
+}

+ 36 - 0
evhttp-sharp/src/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle ("EvHttpSharpBenchmark")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("EvHttpSharpBenchmark")]
+[assembly: AssemblyCopyright ("Copyright ©  2013")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible (false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid ("8c379b35-c45f-4d4b-b54a-2a38ec7112d4")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion ("1.0.0.0")]
+[assembly: AssemblyFileVersion ("1.0.0.0")]

BIN
evhttp-sharp/src/lib/EvHttpSharp.dll


BIN
evhttp-sharp/src/lib/Newtonsoft.Json.dll


BIN
evhttp-sharp/src/lib/libevent_core-2-0-5.dll


BIN
evhttp-sharp/src/lib/libevent_extra-2-0-5.dll


BIN
evhttp-sharp/src/libevent_core-2-0-5.dll


BIN
evhttp-sharp/src/libevent_extra-2-0-5.dll


+ 12 - 12
express/setup.py

@@ -4,37 +4,37 @@ import sys
 import setup_util
 import setup_util
 import os
 import os
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("express/app.js", "mongodb:\/\/.*\/hello_world", "mongodb://" + args.database_host + "/hello_world")
   setup_util.replace_text("express/app.js", "mongodb:\/\/.*\/hello_world", "mongodb://" + args.database_host + "/hello_world")
   setup_util.replace_text("express/app.js", "localhost", args.database_host)
   setup_util.replace_text("express/app.js", "localhost", args.database_host)
 
 
   try:
   try:
-    npm()
+    npm(logfile,errfile)
     if os.name == 'nt':
     if os.name == 'nt':
-      subprocess.Popen("set NODE_ENV=production", shell=True)
-      subprocess.Popen("node app", shell=True, cwd="express")
+      subprocess.Popen("set NODE_ENV=production", shell=True, stderr=errfile, stdout=logfile)
+      subprocess.Popen("node app", shell=True, cwd="express", stderr=errfile, stdout=logfile)
     else:
     else:
-      subprocess.Popen("NODE_ENV=production node app", shell=True, cwd="express")
+      subprocess.Popen("NODE_ENV=production node app", shell=True, cwd="express", stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
 
 
-def npm():
+def npm(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.check_call("copy package.json package.json.dist /y > NUL", shell=True, cwd="express")
+    subprocess.check_call("copy package.json package.json.dist /y > NUL", shell=True, cwd="express", stderr=errfile, stdout=logfile)
     setup_util.replace_text("express/package.json", ".*mysql.*", "")
     setup_util.replace_text("express/package.json", ".*mysql.*", "")
     setup_util.replace_text("express/package.json", ".*mapper.*", "")
     setup_util.replace_text("express/package.json", ".*mapper.*", "")
   
   
   try:
   try:
-    subprocess.check_call("npm install", shell=True, cwd="express")
+    subprocess.check_call("npm install", shell=True, cwd="express", stderr=errfile, stdout=logfile)
   finally:
   finally:
     if os.name == 'nt':
     if os.name == 'nt':
-      subprocess.check_call("del package.json", shell=True, cwd="express")
-      subprocess.check_call("ren package.json.dist package.json", shell=True, cwd="express")
+      subprocess.check_call("del package.json", shell=True, cwd="express", stderr=errfile, stdout=logfile)
+      subprocess.check_call("ren package.json.dist package.json", shell=True, cwd="express", stderr=errfile, stdout=logfile)
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.Popen("taskkill /f /im node.exe > NUL", shell=True)
+    subprocess.Popen("taskkill /f /im node.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   
   
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)

+ 3 - 3
falcon/setup.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     proc = subprocess.Popen([
     proc = subprocess.Popen([
         bin_dir + "/gunicorn",
         bin_dir + "/gunicorn",
@@ -18,10 +18,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="falcon")
+        cwd="falcon", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     out, err = p.communicate()
     out, err = p.communicate()
     for line in out.splitlines():
     for line in out.splitlines():

+ 3 - 3
falcon/setup_py3.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     proc = subprocess.Popen([
     proc = subprocess.Popen([
         bin_dir + "/gunicorn",
         bin_dir + "/gunicorn",
@@ -18,10 +18,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="falcon")
+        cwd="falcon", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 3 - 3
falcon/setup_pypy.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     proc = subprocess.Popen([
     proc = subprocess.Popen([
         bin_dir + "/gunicorn",
         bin_dir + "/gunicorn",
@@ -18,10 +18,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="falcon")
+        cwd="falcon", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 9 - 9
falcore/setup.py

@@ -3,27 +3,27 @@ import sys
 import os
 import os
 import setup_util
 import setup_util
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("falcore/src/framework_benchmarks/falcore.go", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
   setup_util.replace_text("falcore/src/framework_benchmarks/falcore.go", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
   if os.name == 'nt':
   if os.name == 'nt':
     #subprocess.call("rmdir /s /q pkg\\windows_amd64", shell=True, cwd="go")
     #subprocess.call("rmdir /s /q pkg\\windows_amd64", shell=True, cwd="go")
     #subprocess.call("rmdir /s /q src\\github.com", shell=True, cwd="go")
     #subprocess.call("rmdir /s /q src\\github.com", shell=True, cwd="go")
     #subprocess.call("del /s /q /f bin\\hello.exe", shell=True, cwd="go")
     #subprocess.call("del /s /q /f bin\\hello.exe", shell=True, cwd="go")
-    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\falcore&& go get ./...", shell=True, cwd="falcore")
-    subprocess.Popen("setup.bat", shell=True, cwd="falcore") 
+    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\falcore&& go get ./...", shell=True, cwd="falcore", stderr=errfile, stdout=logfile)
+    subprocess.Popen("setup.bat", shell=True, cwd="falcore", stderr=errfile, stdout=logfile) 
     return 0
     return 0
-  subprocess.call("go get ./...", shell=True, cwd="falcore") 
-  subprocess.Popen("go run src/framework_benchmarks/falcore.go".rsplit(" "), cwd="falcore")
+  subprocess.call("go get ./...", shell=True, cwd="falcore", stderr=errfile, stdout=logfile) 
+  subprocess.Popen("go run src/framework_benchmarks/falcore.go".rsplit(" "), cwd="falcore", stderr=errfile, stdout=logfile)
   return 0
   return 0
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.call("taskkill /f /im go.exe > NUL", shell=True)
-    subprocess.call("taskkill /f /im falcore.exe > NUL", shell=True)
+    subprocess.call("taskkill /f /im go.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call("taskkill /f /im falcore.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   out, err = p.communicate()
   out, err = p.communicate()
   for line in out.splitlines():
   for line in out.splitlines():
-    if 'falcore' in line:
+    if 'falcore' in line and 'run-tests' not in line:
       pid = int(line.split(None, 2)[1])
       pid = int(line.split(None, 2)[1])
       os.kill(pid, 9)
       os.kill(pid, 9)
   return 0
   return 0

+ 1 - 3
falcore/src/framework_benchmarks/falcore.go

@@ -31,11 +31,9 @@ type Fortune struct {
 	Message string `json:"message"`
 	Message string `json:"message"`
 }
 }
 
 
-// TODO: remove ?charset=utf8 from DSN after the next Go-MySQL-Driver release
-// https://github.com/go-sql-driver/mysql#unicode-support
 const (
 const (
 	// Database
 	// Database
-	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?charset=utf8"
+	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world"
 	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
 	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
 	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
 	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
 	fortuneSelect      = "SELECT id, message FROM Fortune;"
 	fortuneSelect      = "SELECT id, message FROM Fortune;"

+ 7 - 7
finagle/setup.py

@@ -4,24 +4,24 @@ import sys
 import time
 import time
 import os
 import os
 
 
-def start(args=None):
+def start(args, logfile, errfile):
 
 
 
 
     if os.name == 'nt':
     if os.name == 'nt':
-      subprocess.check_call("..\\sbt\\sbt.bat update compile", shell=True, cwd="finagle")
-      subprocess.Popen("..\\sbt\\sbt.bat -Ddb.host=" + args.database_host + " run", cwd="finagle", shell=True)
+      subprocess.check_call("..\\sbt\\sbt.bat update compile", shell=True, cwd="finagle", stderr=errfile, stdout=logfile)
+      subprocess.Popen("..\\sbt\\sbt.bat -Ddb.host=" + args.database_host + " run", cwd="finagle", shell=True, stderr=errfile, stdout=logfile)
     else:
     else:
-      subprocess.check_call("../sbt/sbt update compile", shell=True, cwd="finagle")
-      subprocess.Popen("../sbt/sbt -Ddb.host=" + args.database_host + " run", cwd="finagle", shell=True)
+      subprocess.check_call("../sbt/sbt update compile", shell=True, cwd="finagle", stderr=errfile, stdout=logfile)
+      subprocess.Popen("../sbt/sbt -Ddb.host=" + args.database_host + " run", cwd="finagle", shell=True, stderr=errfile, stdout=logfile)
 
 
     time.sleep(5)
     time.sleep(5)
     return 0
     return 0
 
 
 
 
 
 
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.check_call("wmic process where \"CommandLine LIKE '%\\\\sbt\\\\sbt%'\" call terminate")
+    subprocess.check_call("wmic process where \"CommandLine LIKE '%\\\\sbt\\\\sbt%'\" call terminate", stderr=errfile, stdout=logfile)
   else:
   else:
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     out, err = p.communicate()
     out, err = p.communicate()

+ 65 - 35
finagle/src/main/scala/com/falmarri/finagle/Finagle.scala

@@ -1,25 +1,40 @@
 package com.falmarri.finagle
 package com.falmarri.finagle
 
 
+import scala.util.Random
+import scala.collection.immutable.StringOps
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.fasterxml.jackson.module.scala.DefaultScalaModule
 import com.fasterxml.jackson.module.scala.DefaultScalaModule
-import com.twitter.finagle.builder.ClientBuilder
-import com.twitter.finagle.exp.mysql.{Client, IntValue, MySQL, Row}
-import com.twitter.finagle.http.{HttpMuxer, Request, Response}
-import com.twitter.finagle.{Http, Service}
-import com.twitter.util.{Future, FuturePool}
-import java.net.InetSocketAddress
 import java.util.concurrent.Executors
 import java.util.concurrent.Executors
+import com.twitter.finagle.Service
+import com.twitter.finagle.exp.Mysql
+import com.twitter.finagle.exp.mysql._
+import org.jboss.netty.handler.codec.http._
+import org.jboss.netty.handler.codec.http.HttpResponseStatus._
+import org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1
 import org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer
 import org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer
-import scala.util.Random
+import com.twitter.util.{Future, FuturePool}
+import java.net.InetSocketAddress
+import com.twitter.finagle.builder.{Server, ServerBuilder}
+import com.twitter.finagle.http.{Http,HttpMuxer}
 
 
 object FinagleBenchmark extends App {
 object FinagleBenchmark extends App {
   val maxConnections = 256
   val maxConnections = 256
 
 
-  val mysql = new Client(ClientBuilder()
-    .codec(new MySQL("benchmarkdbuser", "benchmarkdbpass", Some("hello_world")))
-    .hosts(new InetSocketAddress(System.getProperty("db.host", "localhost"), 3306))
-    .hostConnectionLimit(maxConnections)
-    .buildFactory())
+  //val mysql = new Client(ClientBuilder()
+  //  .codec(new MySQL("benchmarkdbuser", "benchmarkdbpass", Some("hello_world")))
+  //  .hosts(new InetSocketAddress(System.getProperty("db.host", "localhost"), 3306))
+  //  .hostConnectionLimit(maxConnections)
+  //  .buildFactory())
+
+  val username = "benchmarkdbuser"
+  val password = "benchmarkdbpass"
+  val db = "hello_world"
+  val host = System.getProperty("db.host", "localhost")
+
+  val mysql = Mysql
+      .withCredentials(username, password)
+      .withDatabase(db)
+      .newRichClient(host + ":3306")
 
 
   val pool = FuturePool(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2))
   val pool = FuturePool(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2))
 
 
@@ -41,37 +56,52 @@ object FinagleBenchmark extends App {
   def serialize(result: Any): Array[Byte] =
   def serialize(result: Any): Array[Byte] =
     mapper.writeValueAsBytes(result)
     mapper.writeValueAsBytes(result)
 
 
-  def createResponse(req: Request, bytes: Array[Byte]) = {
+  def createResponse(req: HttpRequest, bytes: Array[Byte]) = {
     val body = wrappedBuffer(bytes)
     val body = wrappedBuffer(bytes)
-    val resp = req.response
-    resp.setContentTypeJson
+    val resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
+    //resp.setContentTypeJson
     resp.setContent(body)
     resp.setContent(body)
-    resp.contentLength = body.readableBytes
+    //resp.contentLength = body.readableBytes
     resp
     resp
   }
   }
 
 
-  HttpMuxer.addRichHandler("/json", new Service[Request, Response] {
-    def apply(req: Request): Future[Response] = pool {
-      createResponse(req, serialize(Map("message" -> "Hello, World!")))
-    }
-  })
-
-  HttpMuxer.addRichHandler("/db", new Service[Request, Response] {
-    val rand = new Random()
-    val sql = "SELECT * FROM world WHERE id = "
+  val muxService = new HttpMuxer()
+    .withHandler("/json", new Service[HttpRequest, HttpResponse] {
+      def apply(req: HttpRequest): Future[HttpResponse] = pool {
+        createResponse(req, serialize(Map("message" -> "Hello, World!")))
+      }
+    })
+    .withHandler("/db", new Service[HttpRequest, HttpResponse] {
+      val rand = new Random()
+      val sql = "SELECT * FROM world WHERE id = "
 
 
-    def apply(req: Request): Future[Response] = {
-      val n = req.params.getIntOrElse("queries", 1)
+      def apply(req: HttpRequest): Future[HttpResponse] = {
+        //val n = req.params.getIntOrElse("queries", 1)
+        val decoder = new QueryStringDecoder(req.getUri())
+        val n = {
+          val queries = decoder.getParameters().get("queries")
+          if(queries == null) {
+            1
+          }
+          else {
+            queries.get(0).toInt
+          }
+        }
 
 
-      val qs = (0 until n) map { i =>
-        mysql.select(sql + rand.nextInt(10000))(rowToMap)
-      }
+        val qs = (0 until n) map { i =>
+          mysql.select(sql + rand.nextInt(10000))(rowToMap)
+        }
 
 
-      Future.collect(qs) map { results =>
-        createResponse(req, serialize(results.flatten))
+        Future.collect(qs) map { results =>
+          createResponse(req, serialize(results.flatten))
+        }
       }
       }
-    }
-  })
+    })
 
 
-  Http.serve(new InetSocketAddress(8080), HttpMuxer)
+  //Http.serve(new InetSocketAddress(8080), HttpMuxer)
+  val server: Server = ServerBuilder()
+    .codec(Http())
+    .bindTo(new InetSocketAddress(8080))
+    .name("HttpServer")
+    .build(muxService)
 }
 }

+ 3 - 3
flask/setup.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     proc = subprocess.Popen([
     proc = subprocess.Popen([
@@ -19,10 +19,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="flask")
+        cwd="flask", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     out, err = p.communicate()
     out, err = p.communicate()
     for line in out.splitlines():
     for line in out.splitlines():

+ 6 - 6
flask/setup_nginxuwsgi.py

@@ -7,23 +7,23 @@ bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
 config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
 config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
 NCPU = multiprocessing.cpu_count()
 NCPU = multiprocessing.cpu_count()
 
 
-def start(args):
+def start(args, logfile, errfile):
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     try:
     try:
         subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c ' +
         subprocess.check_call('sudo /usr/local/nginx/sbin/nginx -c ' +
-            config_dir + '/nginx_uwsgi.conf', shell=True)
+            config_dir + '/nginx_uwsgi.conf', shell=True, stderr=errfile, stdout=logfile)
         # Run in the background, but keep stdout/stderr for easy debugging
         # Run in the background, but keep stdout/stderr for easy debugging
         subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
         subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
             ' --processes ' + str(NCPU * 3) +
             ' --processes ' + str(NCPU * 3) +
             ' --wsgi app:app',
             ' --wsgi app:app',
-            shell=True, cwd='flask')
+            shell=True, cwd='flask', stderr=errfile, stdout=logfile)
         return 0
         return 0
     except subprocess.CalledProcessError:
     except subprocess.CalledProcessError:
         return 1
         return 1
 
 
-def stop():
-    subprocess.call('sudo /usr/local/nginx/sbin/nginx -s stop', shell=True)
-    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True)
+def stop(logfile, errfile):
+    subprocess.call('sudo /usr/local/nginx/sbin/nginx -s stop', shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True, stderr=errfile, stdout=logfile)
 
 
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
     out, err = p.communicate()
     out, err = p.communicate()

+ 3 - 3
flask/setup_py3.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     proc = subprocess.Popen([
     proc = subprocess.Popen([
@@ -19,10 +19,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="flask")
+        cwd="flask", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 3 - 3
flask/setup_pypy.py

@@ -9,7 +9,7 @@ NCPU = multiprocessing.cpu_count()
 proc = None
 proc = None
 
 
 
 
-def start(args):
+def start(args, logfile, errfile):
     global proc
     global proc
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     proc = subprocess.Popen([
     proc = subprocess.Popen([
@@ -19,10 +19,10 @@ def start(args):
         "-b", "0.0.0.0:8080",
         "-b", "0.0.0.0:8080",
         '-w', str(NCPU*3),
         '-w', str(NCPU*3),
         "--log-level=critical"],
         "--log-level=critical"],
-        cwd="flask")
+        cwd="flask", stderr=errfile, stdout=logfile)
     return 0
     return 0
 
 
-def stop():
+def stop(logfile, errfile):
     global proc
     global proc
     if proc is None:
     if proc is None:
         return 0
         return 0

+ 7 - 6
gemini/setup.py

@@ -6,20 +6,21 @@ from os.path import expanduser
 
 
 home = expanduser("~")
 home = expanduser("~")
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("gemini/Docroot/WEB-INF/GeminiHello.conf", "db.ConnectString = .*:3306", "db.ConnectString = " + args.database_host + ":3306")
   setup_util.replace_text("gemini/Docroot/WEB-INF/GeminiHello.conf", "db.ConnectString = .*:3306", "db.ConnectString = " + args.database_host + ":3306")
   setup_util.replace_text("gemini/Docroot/WEB-INF/resin.xml", "root-directory=\".*\/FrameworkBenchmarks", "root-directory=\"" + home + "/FrameworkBenchmarks")
   setup_util.replace_text("gemini/Docroot/WEB-INF/resin.xml", "root-directory=\".*\/FrameworkBenchmarks", "root-directory=\"" + home + "/FrameworkBenchmarks")
   
   
   try:
   try:
-    subprocess.call("mkdir classes", shell=True, cwd="gemini/Docroot/WEB-INF")
-    subprocess.check_call("ant compile", shell=True, cwd="gemini")
-    subprocess.check_call("$RESIN_HOME/bin/resinctl -conf $HOME/FrameworkBenchmarks/gemini/Docroot/WEB-INF/resin.xml start", shell=True)
+    # This was reporting an error because it already exists... not sure.
+    #subprocess.call("mkdir classes", shell=True, cwd="gemini/Docroot/WEB-INF", stderr=errfile, stdout=logfile)
+    subprocess.check_call("ant compile", shell=True, cwd="gemini", stderr=errfile, stdout=logfile)
+    subprocess.check_call("$RESIN_HOME/bin/resinctl -conf $HOME/FrameworkBenchmarks/gemini/Docroot/WEB-INF/resin.xml start", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1
-def stop():
+def stop(logfile, errfile):
   try:
   try:
-    subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True)
+    subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   except subprocess.CalledProcessError:
   except subprocess.CalledProcessError:
     return 1
     return 1

+ 8 - 8
go/setup.py

@@ -3,22 +3,22 @@ import sys
 import os
 import os
 import setup_util
 import setup_util
 
 
-def start(args):
+def start(args, logfile, errfile):
   setup_util.replace_text("go/src/hello/hello.go", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
   setup_util.replace_text("go/src/hello/hello.go", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
   if os.name == 'nt':
   if os.name == 'nt':
     #subprocess.call("rmdir /s /q pkg\\windows_amd64", shell=True, cwd="go")
     #subprocess.call("rmdir /s /q pkg\\windows_amd64", shell=True, cwd="go")
     #subprocess.call("rmdir /s /q src\\github.com", shell=True, cwd="go")
     #subprocess.call("rmdir /s /q src\\github.com", shell=True, cwd="go")
     #subprocess.call("del /s /q /f bin\\hello.exe", shell=True, cwd="go")
     #subprocess.call("del /s /q /f bin\\hello.exe", shell=True, cwd="go")
-    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\go&& go get ./...", shell=True, cwd="go")
-    subprocess.Popen("setup.bat", shell=True, cwd="go") 
+    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\go&& go get ./...", shell=True, cwd="go", stderr=errfile, stdout=logfile)
+    subprocess.Popen("setup.bat", shell=True, cwd="go", stderr=errfile, stdout=logfile) 
     return 0
     return 0
-  subprocess.call("go get ./...", shell=True, cwd="go") 
-  subprocess.Popen("go run src/hello/hello.go".rsplit(" "), cwd="go")
+  subprocess.call("go get ./...", shell=True, cwd="go", stderr=errfile, stdout=logfile) 
+  subprocess.Popen("go run src/hello/hello.go".rsplit(" "), cwd="go", stderr=errfile, stdout=logfile)
   return 0
   return 0
-def stop():
+def stop(logfile, errfile):
   if os.name == 'nt':
   if os.name == 'nt':
-    subprocess.call("taskkill /f /im go.exe > NUL", shell=True)
-    subprocess.call("taskkill /f /im hello.exe > NUL", shell=True)
+    subprocess.call("taskkill /f /im go.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call("taskkill /f /im hello.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
     return 0
     return 0
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
   out, err = p.communicate()
   out, err = p.communicate()

+ 1 - 3
go/src/hello/hello.go

@@ -28,11 +28,9 @@ type Fortune struct {
 	Message string `json:"message"`
 	Message string `json:"message"`
 }
 }
 
 
-// TODO: remove ?charset=utf8 from DSN after the next Go-MySQL-Driver release
-// https://github.com/go-sql-driver/mysql#unicode-support
 const (
 const (
 	// Database
 	// Database
-	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?charset=utf8"
+	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world"
 	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
 	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
 	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
 	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
 	fortuneSelect      = "SELECT id, message FROM Fortune;"
 	fortuneSelect      = "SELECT id, message FROM Fortune;"

+ 21 - 4
grails/README.md

@@ -4,17 +4,34 @@ This is the Grails portion of a [benchmarking test suite](../) comparing a varie
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 The tests were run with:
 The tests were run with:
-* [Grails 2.3.1](http://grails.org/)
+* [Grails 2.3.3](http://grails.org/)
 * [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
 * [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
 * [Resin 4.0.34](http://www.caucho.com/)
 * [Resin 4.0.34](http://www.caucho.com/)
 * [MySQL 5.5.29](https://dev.mysql.com/)
 * [MySQL 5.5.29](https://dev.mysql.com/)
 
 
 
 
 ## Test URLs
 ## Test URLs
-### JSON Encoding Test
+
+### Test type 1: JSON serialization
 
 
 http://localhost:8080/grails/hello/json
 http://localhost:8080/grails/hello/json
 
 
-### Data-Store/Database Mapping Test
+### Test type 2: Single database query
+
+http://localhost:8080/grails/hello/db
+
+### Test type 3: Multiple database queries
+
+http://localhost:8080/grails/hello/queries?queries=10
+
+### Test type 4: Fortunes
+
+http://localhost:8080/grails/hello/fortunes
+
+### Test type 5: Database updates
+
+http://localhost:8080/grails/hello/updates?queries=10
+
+### Test type 6: Plaintext
 
 
-http://localhost:8080/grails/hello/db?queries=5
+http://localhost:8080/grails/hello/plaintext

+ 5 - 3
grails/benchmark_config

@@ -5,7 +5,10 @@
       "setup_file": "setup",
       "setup_file": "setup",
       "json_url": "/grails/hello/json",
       "json_url": "/grails/hello/json",
       "db_url": "/grails/hello/db",
       "db_url": "/grails/hello/db",
-      "query_url": "/grails/hello/db?queries=",
+      "query_url": "/grails/hello/queries?queries=",
+      "fortune_url": "/grails/hello/fortunes",
+      "update_url": "/grails/hello/updates?queries=",
+      "plaintext_url": "/grails/hello/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
@@ -19,8 +22,7 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "grails",
       "display_name": "grails",
       "notes": "",
       "notes": "",
-      "versus": "servlet",
-      "skip": "true"
+      "versus": "servlet"
     }
     }
   }]
   }]
 }
 }

+ 15 - 13
grails/hello/.classpath

@@ -1,14 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 <classpath>
-    <classpathentry kind="src" path="src/java"/>
-    <classpathentry kind="src" path="src/groovy"/>
-    <classpathentry kind="src" path="grails-app/conf"/>
-    <classpathentry kind="src" path="grails-app/controllers"/>
-    <classpathentry kind="src" path="grails-app/domain"/>
-    <classpathentry kind="src" path="grails-app/services"/>
-    <classpathentry kind="src" path="grails-app/taglib"/>
-    <classpathentry kind="src" path="test/integration"/>
-    <classpathentry kind="src" path="test/unit"/>
-    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-    <classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
-    <classpathentry kind="output" path="web-app/WEB-INF/classes"/>
-</classpath>
+	<classpathentry kind="src" path="src/java"/>
+	<classpathentry kind="src" path="src/groovy"/>
+	<classpathentry kind="src" path="grails-app/conf"/>
+	<classpathentry kind="src" path="grails-app/controllers"/>
+	<classpathentry kind="src" path="grails-app/domain"/>
+	<classpathentry kind="src" path="grails-app/services"/>
+	<classpathentry kind="src" path="grails-app/taglib"/>
+	<classpathentry kind="src" path="grails-app/utils"/>
+	<classpathentry kind="src" path="test/integration"/>
+	<classpathentry kind="src" path="test/unit"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.grails.ide.eclipse.core.CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target-eclipse/classes"/>
+</classpath>

+ 17 - 0
grails/hello/.gitignore

@@ -0,0 +1,17 @@
+*.iws
+*Db.properties
+*Db.script
+.settings
+stacktrace.log
+/*.zip
+/plugin.xml
+/*.log
+/*DB.*
+/cobertura.ser
+.DS_Store
+/target/
+/out/
+/web-app/plugins
+/web-app/WEB-INF/classes
+/.link_to_grails_plugins/
+/target-eclipse/

+ 7 - 1
grails/hello/.project

@@ -5,6 +5,11 @@
 	<projects>
 	<projects>
 	</projects>
 	</projects>
 	<buildSpec>
 	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 		<buildCommand>
 		<buildCommand>
 			<name>org.eclipse.jdt.core.javabuilder</name>
 			<name>org.eclipse.jdt.core.javabuilder</name>
 			<arguments>
 			<arguments>
@@ -12,8 +17,9 @@
 		</buildCommand>
 		</buildCommand>
 	</buildSpec>
 	</buildSpec>
 	<natures>
 	<natures>
-	    <nature>com.springsource.sts.grails.core.nature</nature>
+		<nature>org.grails.ide.eclipse.core.nature</nature>
 		<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
 		<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
 	</natures>
 	</natures>
 </projectDescription>
 </projectDescription>

+ 2 - 2
grails/hello/application.properties

@@ -1,6 +1,6 @@
 #Grails Metadata file
 #Grails Metadata file
-#Sat Oct 26 13:32:20 PDT 2013
-app.grails.version=2.3.1
+#Sun Oct 20 22:08:59 EEST 2013
+app.grails.version=2.3.3
 app.name=hello
 app.name=hello
 app.servlet.version=2.5
 app.servlet.version=2.5
 app.version=0.1
 app.version=0.1

+ 32 - 5
grails/hello/grails-app/conf/BuildConfig.groovy

@@ -2,10 +2,34 @@ grails.servlet.version = "2.5" // Change depending on target container complianc
 grails.project.class.dir = "target/classes"
 grails.project.class.dir = "target/classes"
 grails.project.test.class.dir = "target/test-classes"
 grails.project.test.class.dir = "target/test-classes"
 grails.project.test.reports.dir = "target/test-reports"
 grails.project.test.reports.dir = "target/test-reports"
+grails.project.work.dir = "target/work"
 grails.project.target.level = 1.7
 grails.project.target.level = 1.7
 grails.project.source.level = 1.7
 grails.project.source.level = 1.7
 //grails.project.war.file = "target/${appName}-${appVersion}.war"
 //grails.project.war.file = "target/${appName}-${appVersion}.war"
 
 
+grails.project.fork = [
+    // configure settings for compilation JVM, note that if you alter the Groovy version forked compilation is required
+    //  compile: [maxMemory: 256, minMemory: 64, debug: false, maxPerm: 256, daemon:true],
+
+    // configure settings for the test-app JVM, uses the daemon by default
+    test: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, daemon:true],
+    // configure settings for the run-app JVM
+    run: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false],
+    // configure settings for the run-war JVM
+    war: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false],
+    // configure settings for the Console UI JVM
+    console: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256]
+]
+
+def yjpConfig = [jvmArgs: [
+        "-agentpath:/opt/yjp/bin/linux-x86-64/libyjpagent.so=delay=30000,disablealloc,disablej2ee,noj2ee,builtinprobes=none,sampling,monitors,onexit=snapshot,telemetryperiod=250"
+    ]]
+if (System.getProperty("grails.yjp")) {
+    grails.project.fork.war += yjpConfig
+    println "Using YJP for run-war"
+}
+
+grails.project.dependency.resolver = "maven" // or ivy
 grails.project.dependency.resolution = {
 grails.project.dependency.resolution = {
     // inherit Grails' default dependencies
     // inherit Grails' default dependencies
     inherits("global") {
     inherits("global") {
@@ -14,11 +38,13 @@ grails.project.dependency.resolution = {
     }
     }
     log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
     log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
     checksums true // Whether to verify checksums on resolve
     checksums true // Whether to verify checksums on resolve
+    legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility
 
 
     repositories {
     repositories {
         inherits true // Whether to inherit repository definitions from plugins
         inherits true // Whether to inherit repository definitions from plugins
         grailsPlugins()
         grailsPlugins()
         grailsHome()
         grailsHome()
+		mavenLocal()
         grailsCentral()
         grailsCentral()
         mavenCentral()
         mavenCentral()
 
 
@@ -33,18 +59,19 @@ grails.project.dependency.resolution = {
     dependencies {
     dependencies {
         // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
         // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
 
 
-        runtime 'mysql:mysql-connector-java:5.1.22'
+        runtime 'mysql:mysql-connector-java:5.1.27'
     }
     }
 
 
     plugins {
     plugins {
-        compile ":hibernate:3.6.10.2"
-        runtime ":jquery:1.7.1"
-        runtime ":resources:1.1.6"
+        runtime ":hibernate:3.6.10.4"
+        //runtime ":jquery:1.10.2"
+        //runtime ":resources:1.2.1"
 
 
         // Uncomment these (or add new ones) to enable additional resources capabilities
         // Uncomment these (or add new ones) to enable additional resources capabilities
         //runtime ":zipped-resources:1.0"
         //runtime ":zipped-resources:1.0"
         //runtime ":cached-resources:1.0"
         //runtime ":cached-resources:1.0"
         //runtime ":yui-minify-resources:0.1.4"
         //runtime ":yui-minify-resources:0.1.4"
-        build ':tomcat:7.0.40.1'
+
+        build ":tomcat:7.0.47"
     }
     }
 }
 }

+ 49 - 42
grails/hello/grails-app/conf/Config.groovy

@@ -12,21 +12,26 @@
 
 
 
 
 grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
 grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
-grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format
-grails.mime.use.accept.header = false
-grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
-                      xml: ['text/xml', 'application/xml'],
-                      text: 'text/plain',
-                      js: 'text/javascript',
-                      rss: 'application/rss+xml',
-                      atom: 'application/atom+xml',
-                      css: 'text/css',
-                      csv: 'text/csv',
-                      all: '*/*',
-                      json: ['application/json','text/json'],
-                      form: 'application/x-www-form-urlencoded',
-                      multipartForm: 'multipart/form-data'
-                    ]
+
+grails.app.context = '/grails'
+
+// The ACCEPT header will not be used for content negotiation for user agents containing the following strings (defaults to the 4 major rendering engines)
+grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
+grails.mime.types = [
+    all:           '*/*',
+    atom:          'application/atom+xml',
+    css:           'text/css',
+    csv:           'text/csv',
+    form:          'application/x-www-form-urlencoded',
+    html:          ['text/html','application/xhtml+xml'],
+    js:            'text/javascript',
+    json:          ['application/json', 'text/json'],
+    multipartForm: 'multipart/form-data',
+    rss:           'application/rss+xml',
+    text:          'text/plain',
+    hal:           ['application/hal+json','application/hal+xml'],
+    xml:           ['text/xml', 'application/xml']
+]
 
 
 // URL Mapping Cache Max Size, defaults to 5000
 // URL Mapping Cache Max Size, defaults to 5000
 //grails.urlmapping.cache.maxsize = 1000
 //grails.urlmapping.cache.maxsize = 1000
@@ -34,10 +39,33 @@ grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
 // What URL patterns should be processed by the resources plugin
 // What URL patterns should be processed by the resources plugin
 grails.resources.adhoc.patterns = ['/images/*', '/css/*', '/js/*', '/plugins/*']
 grails.resources.adhoc.patterns = ['/images/*', '/css/*', '/js/*', '/plugins/*']
 
 
+// Legacy setting for codec used to encode data with ${}
+grails.views.default.codec = "html"
+
+// The default scope for controllers. May be prototype, session or singleton.
+// If unspecified, controllers are prototype scoped.
+grails.controllers.defaultScope = 'singleton'
 
 
-// The default codec used to encode data with ${}
-grails.views.default.codec = "none" // none, html, base64
-grails.views.gsp.encoding = "UTF-8"
+// GSP settings
+grails {
+    views {
+        gsp {
+            encoding = 'UTF-8'
+            htmlcodec = 'xml' // use xml escaping instead of HTML4 escaping
+            codecs {
+                expression = 'html' // escapes values inside ${}
+                scriptlet = 'html' // escapes output from scriptlets in GSPs
+                taglib = 'none' // escapes output from taglibs
+                staticparts = 'none' // escapes output from static template parts
+            }
+        }
+        // escapes all not-encoded output at final stage of outputting
+        filteringCodecForContentType {
+            //'text/html' = 'html'
+        }
+    }
+}
+ 
 grails.converters.encoding = "UTF-8"
 grails.converters.encoding = "UTF-8"
 // enable Sitemesh preprocessing of GSP pages
 // enable Sitemesh preprocessing of GSP pages
 grails.views.gsp.sitemesh.preprocess = true
 grails.views.gsp.sitemesh.preprocess = true
@@ -59,6 +87,9 @@ grails.exceptionresolver.params.exclude = ['password']
 // disabling query cache
 // disabling query cache
 grails.hibernate.cache.queries = false
 grails.hibernate.cache.queries = false
 
 
+// OSIV is readonly by default
+grails.hibernate.osiv.readonly = true
+
 // set per-environment serverURL stem for creating absolute links
 // set per-environment serverURL stem for creating absolute links
 environments {
 environments {
     development {
     development {
@@ -91,27 +122,3 @@ log4j = {
            'org.hibernate',
            'org.hibernate',
            'net.sf.ehcache.hibernate'
            'net.sf.ehcache.hibernate'
 }
 }
-
-// Uncomment and edit the following lines to start using Grails encoding & escaping improvements
-
-/* remove this line 
-// GSP settings
-grails {
-    views {
-        gsp {
-            encoding = 'UTF-8'
-            htmlcodec = 'xml' // use xml escaping instead of HTML4 escaping
-            codecs {
-                expression = 'html' // escapes values inside null
-                scriptlet = 'none' // escapes output from scriptlets in GSPs
-                taglib = 'none' // escapes output from taglibs
-                staticparts = 'none' // escapes output from static template parts
-            }
-        }
-        // escapes all not-encoded output at final stage of outputting
-        filteringCodecForContentType {
-            //'text/html' = 'html'
-        }
-    }
-}
-remove this line */

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