Browse Source

Merge branch 'master' into remove-testrunner

msmith-techempower 8 years ago
parent
commit
c79e021dd9
60 changed files with 1013 additions and 511 deletions
  1. 1 1
      .travis.yml
  2. 4 2
      frameworks/C/h2o/src/database.c
  3. 8 5
      frameworks/C/h2o/src/database.h
  4. 2 1
      frameworks/C/h2o/src/fortune.c
  5. 419 239
      frameworks/C/h2o/src/world.c
  6. 7 8
      frameworks/CSharp/revenj/Revenj.Bench/RestService.cs
  7. 2 2
      frameworks/Elixir/phoenix/benchmark_config.json
  8. 4 4
      frameworks/Java/grizzly-bm/pom.xml
  9. 4 4
      frameworks/Java/grizzly-jersey/pom.xml
  10. 23 5
      frameworks/Java/rapidoid/benchmark_config.json
  11. 7 1
      frameworks/Java/rapidoid/pom.xml
  12. 12 0
      frameworks/Java/rapidoid/setup-default.sh
  13. 0 8
      frameworks/Java/rapidoid/setup-high-level.sh
  14. 12 0
      frameworks/Java/rapidoid/setup-http-fast.sh
  15. 0 8
      frameworks/Java/rapidoid/setup-low-level.sh
  16. 12 0
      frameworks/Java/rapidoid/setup-mysql.sh
  17. 12 0
      frameworks/Java/rapidoid/setup-postgres.sh
  18. 11 4
      frameworks/Java/rapidoid/src/main/java/common/Helper.java
  19. 32 13
      frameworks/Java/rapidoid/src/main/java/highlevel/Main.java
  20. 8 0
      frameworks/Java/rapidoid/src/main/java/lowlevel/Main.java
  21. 7 9
      frameworks/Java/rapidoid/src/main/java/lowlevel/PlaintextAndJsonServer.java
  22. 7 12
      frameworks/Java/wicket/README.md
  23. 13 32
      frameworks/Java/wicket/pom.xml
  24. 1 2
      frameworks/Java/wicket/setup.sh
  25. 0 8
      frameworks/Java/wicket/src/main/java/hellowicket/BasePage.html
  26. 0 8
      frameworks/Java/wicket/src/main/java/hellowicket/BasePage.java
  27. 21 19
      frameworks/Java/wicket/src/main/java/hellowicket/HelloDbResponse.java
  28. 22 14
      frameworks/Java/wicket/src/main/java/hellowicket/HelloJsonResponse.java
  29. 15 0
      frameworks/Java/wicket/src/main/java/hellowicket/JsonMessage.java
  30. 50 0
      frameworks/Java/wicket/src/main/java/hellowicket/RequestMapper.java
  31. 7 20
      frameworks/Java/wicket/src/main/java/hellowicket/WicketApplication.java
  32. 14 11
      frameworks/Java/wicket/src/main/java/hellowicket/dbupdates/HelloDbUpdatesResource.java
  33. 12 1
      frameworks/Java/wicket/src/main/java/hellowicket/fortune/FortunePage.java
  34. 11 5
      frameworks/Java/wicket/src/main/java/hellowicket/plaintext/HelloTextResource.java
  35. 0 14
      frameworks/Java/wicket/src/main/webapp/WEB-INF/resin-web.xml
  36. 1 1
      frameworks/Python/bottle/requirements.txt
  37. 1 1
      frameworks/Python/django/requirements.txt
  38. 1 1
      frameworks/Python/falcon/requirements.txt
  39. 1 1
      frameworks/Python/flask/requirements.txt
  40. 1 1
      frameworks/Python/pyramid/requirements.txt
  41. 1 1
      frameworks/Python/turbogears/requirements.txt
  42. 1 1
      frameworks/Python/web2py/requirements.txt
  43. 1 1
      frameworks/Python/weppy/requirements.txt
  44. 1 1
      frameworks/Python/wheezyweb/requirements.txt
  45. 1 1
      frameworks/Python/wsgi/requirements.txt
  46. 2 2
      frameworks/Scala/akka-http/build.sbt
  47. 3 0
      frameworks/Scala/fintrospect/Dockerfile
  48. 7 7
      frameworks/Scala/fintrospect/README.md
  49. 5 1
      frameworks/Scala/fintrospect/benchmark_config.json
  50. 6 4
      frameworks/Scala/fintrospect/build.sbt
  51. 11 0
      frameworks/Scala/fintrospect/run_db.sh
  52. 2 0
      frameworks/Scala/fintrospect/setup.sh
  53. 5 0
      frameworks/Scala/fintrospect/source_code
  54. 22 0
      frameworks/Scala/fintrospect/src/main/scala/Database.scala
  55. 81 0
      frameworks/Scala/fintrospect/src/main/scala/DatabaseRoutes.scala
  56. 24 26
      frameworks/Scala/fintrospect/src/main/scala/FintrospectBenchmarkServer.scala
  57. 42 0
      frameworks/Scala/fintrospect/src/main/scala/FortunesRoute.scala
  58. 15 0
      frameworks/Scala/fintrospect/src/main/scala/JsonRoute.scala
  59. 17 0
      frameworks/Scala/fintrospect/src/main/scala/PlainTextRoute.scala
  60. 1 1
      toolset/setup/linux/webservers/resin.sh

+ 1 - 1
.travis.yml

@@ -210,12 +210,12 @@ services:
   - postgresql
   - postgresql
   - redis-server
   - redis-server
   - mongodb
   - mongodb
+  - mysql
 
 
 addons:
 addons:
   postgresql: "9.3"
   postgresql: "9.3"
   apt:
   apt:
     packages:
     packages:
-      - mysql-server
       - redis-server
       - redis-server
 
 
 before_script:
 before_script:

+ 4 - 2
frameworks/C/h2o/src/database.c

@@ -66,8 +66,10 @@ static const struct {
 } prepared_statement[] = {
 } prepared_statement[] = {
 	{FORTUNE_TABLE_NAME, "SELECT * FROM " FORTUNE_TABLE_NAME ";"},
 	{FORTUNE_TABLE_NAME, "SELECT * FROM " FORTUNE_TABLE_NAME ";"},
 	{WORLD_TABLE_NAME,
 	{WORLD_TABLE_NAME,
-	 "SELECT * FROM " WORLD_TABLE_NAME " "
-	 "WHERE " WORLD_TABLE_NAME "." ID_FIELD_NAME " = $1::integer;"},
+	 "SELECT * FROM " WORLD_TABLE_NAME " WHERE " ID_FIELD_NAME " = $1::integer;"},
+	{UPDATE_QUERY_NAME,
+	 "UPDATE " WORLD_TABLE_NAME " SET randomNumber = $2::integer "
+	 "WHERE " ID_FIELD_NAME " = $1::integer;"},
 };
 };
 
 
 static void do_execute_query(db_conn_t *db_conn)
 static void do_execute_query(db_conn_t *db_conn)

+ 8 - 5
frameworks/C/h2o/src/database.h

@@ -22,12 +22,13 @@
 #define DATABASE_H_
 #define DATABASE_H_
 
 
 #include <h2o.h>
 #include <h2o.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <postgresql/libpq-fe.h>
 #include <postgresql/libpq-fe.h>
 
 
 #include "list.h"
 #include "list.h"
+#include "utility.h"
 
 
-#define COPY_HEADER "PGCOPY\n\377\r\n\0\0\0\0\0\0\0\0\0"
 #define DB_REQ_ERROR "too many concurrent database requests\n"
 #define DB_REQ_ERROR "too many concurrent database requests\n"
 #define DB_TIMEOUT_ERROR "database timeout\n"
 #define DB_TIMEOUT_ERROR "database timeout\n"
 #define FORTUNE_TABLE_NAME "Fortune"
 #define FORTUNE_TABLE_NAME "Fortune"
@@ -36,13 +37,15 @@
 #define IS_SINGLE_ROW 2
 #define IS_SINGLE_ROW 2
 #define MAX_ID 10000
 #define MAX_ID 10000
 #define MESSAGE_FIELD_NAME "message"
 #define MESSAGE_FIELD_NAME "message"
+#define UPDATE_QUERY_NAME "Update"
 #define WORLD_TABLE_NAME "World"
 #define WORLD_TABLE_NAME "World"
 
 
 #define UPDATE_QUERY \
 #define UPDATE_QUERY \
-	"CREATE TEMP TABLE input(LIKE World INCLUDING ALL) ON COMMIT DROP;" \
-	"COPY input FROM STDIN WITH (FORMAT binary);" \
-	"UPDATE World SET randomNumber = input.randomNumber FROM input " \
-	"WHERE World." ID_FIELD_NAME " = input." ID_FIELD_NAME ";"
+	"EXECUTE \"" WORLD_TABLE_NAME "\"(%" PRIu32 ");" \
+	"EXECUTE \"" UPDATE_QUERY_NAME "\"(%" PRIu32 ", %" PRIu32 ");"
+
+#define MAX_UPDATE_QUERY_LEN \
+	(sizeof(UPDATE_QUERY) + 3 * (sizeof(MKSTR(MAX_ID)) - 1) - 3 * sizeof(PRIu32))
 
 
 typedef enum {
 typedef enum {
 	SUCCESS,
 	SUCCESS,

+ 2 - 1
frameworks/C/h2o/src/fortune.c

@@ -102,6 +102,7 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	}
 	}
 
 
 	if (ret) {
 	if (ret) {
+		memset(iovec_list->iov + iovec_list->iovcnt, 0, sizeof(*iovec_list->iov));
 		iovec_list->iov[iovec_list->iovcnt].base = (char *) buffer;
 		iovec_list->iov[iovec_list->iovcnt].base = (char *) buffer;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
 		fortune_ctx->content_length += buffer_size;
 		fortune_ctx->content_length += buffer_size;
@@ -263,7 +264,7 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 		const size_t iovcnt = MIN(MAX_IOVEC, fortune_ctx->num_result * 5 + 2);
 		const size_t iovcnt = MIN(MAX_IOVEC, fortune_ctx->num_result * 5 + 2);
 		const size_t sz = offsetof(iovec_list_t, iov) + iovcnt * sizeof(h2o_iovec_t);
 		const size_t sz = offsetof(iovec_list_t, iov) + iovcnt * sizeof(h2o_iovec_t);
 		char _Alignas(iovec_list_t) mem[sz];
 		char _Alignas(iovec_list_t) mem[sz];
-		iovec_list_t * const iovec_list = (iovec_list_t *) mem;
+		iovec_list_t * const restrict iovec_list = (iovec_list_t *) mem;
 
 
 		memset(iovec_list, 0, offsetof(iovec_list_t, iov));
 		memset(iovec_list, 0, offsetof(iovec_list_t, iov));
 		iovec_list->max_iovcnt = iovcnt;
 		iovec_list->max_iovcnt = iovcnt;

+ 419 - 239
frameworks/C/h2o/src/world.c

@@ -19,8 +19,10 @@
 
 
 #include <assert.h>
 #include <assert.h>
 #include <h2o.h>
 #include <h2o.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <postgresql/libpq-fe.h>
 #include <postgresql/libpq-fe.h>
 #include <yajl/yajl_gen.h>
 #include <yajl/yajl_gen.h>
@@ -38,13 +40,8 @@
 #define QUERIES_PARAMETER "queries="
 #define QUERIES_PARAMETER "queries="
 #define RANDOM_NUM_KEY "randomNumber"
 #define RANDOM_NUM_KEY "randomNumber"
 
 
-typedef enum {
-	NO_UPDATE = 0,
-	CREATE,
-	COPY_1,
-	COPY_2,
-	UPDATE
-} update_state_t;
+typedef struct multiple_query_ctx_t multiple_query_ctx_t;
+typedef struct update_ctx_t update_ctx_t;
 
 
 typedef struct {
 typedef struct {
 	uint32_t id;
 	uint32_t id;
@@ -52,41 +49,72 @@ typedef struct {
 } query_result_t;
 } query_result_t;
 
 
 typedef struct {
 typedef struct {
+	multiple_query_ctx_t *ctx;
+	const char *id_pointer;
+	uint32_t id;
+	int id_format;
+	int id_len;
 	db_query_param_t param;
 	db_query_param_t param;
+} query_param_t;
+
+typedef struct {
 	const char *id_pointer;
 	const char *id_pointer;
 	h2o_req_t *req;
 	h2o_req_t *req;
 	uint32_t id;
 	uint32_t id;
 	int id_format;
 	int id_format;
 	int id_len;
 	int id_len;
+	db_query_param_t param;
 } single_query_ctx_t;
 } single_query_ctx_t;
 
 
+struct multiple_query_ctx_t {
+	yajl_gen gen; // also an error flag
+	h2o_req_t *req;
+	query_param_t *query_param;
+	size_t num_query;
+	size_t num_query_in_progress;
+	size_t num_result;
+	query_result_t res[];
+};
+
 typedef struct {
 typedef struct {
-	single_query_ctx_t single;
-	yajl_gen gen;
+	char *command;
+	update_ctx_t *ctx;
+	uint32_t random_number;
+	bool update;
+	db_query_param_t param;
+} update_param_t;
+
+struct update_ctx_t {
+	yajl_gen gen; // also an error flag
+	h2o_req_t *req;
+	update_param_t *update_param;
 	size_t num_query;
 	size_t num_query;
+	size_t num_query_in_progress;
 	size_t num_result;
 	size_t num_result;
-	update_state_t update_state;
 	query_result_t res[];
 	query_result_t res[];
-} multiple_query_ctx_t;
-
-static void cleanup_request(void *data);
-static int do_multiple_queries(update_state_t update_state, h2o_req_t *req);
-static void initialize_single_query_context(h2o_req_t *req,
-                                            on_result_t on_result,
-                                            single_query_ctx_t *query_ctx);
-static void on_database_error(db_query_param_t *param, const char *error_string);
-static void on_database_timeout(db_query_param_t *param);
+};
+
+static void cleanup_multiple_query(void *data);
+static void cleanup_update(void *data);
+static size_t get_query_number(h2o_req_t *req);
+static void initialize_ids(size_t num_query, query_result_t *res, unsigned int *seed);
+static void on_multiple_query_error(db_query_param_t *param, const char *error_string);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
+static void on_multiple_query_timeout(db_query_param_t *param);
+static void on_single_query_error(db_query_param_t *param, const char *error_string);
 static result_return_t on_single_query_result(db_query_param_t *param, PGresult *result);
 static result_return_t on_single_query_result(db_query_param_t *param, PGresult *result);
+static void on_single_query_timeout(db_query_param_t *param);
+static void on_update_error(db_query_param_t *param, const char *error_string);
 static result_return_t on_update_result(db_query_param_t *param, PGresult *result);
 static result_return_t on_update_result(db_query_param_t *param, PGresult *result);
-static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn);
+static void on_update_timeout(db_query_param_t *param);
+static void process_result(PGresult *result, query_result_t *out);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static void serialize_items(const query_result_t *res,
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
                             size_t num_result,
                             yajl_gen gen,
                             yajl_gen gen,
                             h2o_req_t *req);
                             h2o_req_t *req);
 
 
-static void cleanup_request(void *data)
+static void cleanup_multiple_query(void *data)
 {
 {
 	const multiple_query_ctx_t * const query_ctx = data;
 	const multiple_query_ctx_t * const query_ctx = data;
 
 
@@ -94,11 +122,16 @@ static void cleanup_request(void *data)
 		yajl_gen_free(query_ctx->gen);
 		yajl_gen_free(query_ctx->gen);
 }
 }
 
 
-static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
+static void cleanup_update(void *data)
+{
+	const update_ctx_t * const update_ctx = data;
+
+	if (update_ctx->gen)
+		yajl_gen_free(update_ctx->gen);
+}
+
+static size_t get_query_number(h2o_req_t *req)
 {
 {
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_ctx,
-	                                                      req->conn->ctx);
 	int num_query = 0;
 	int num_query = 0;
 
 
 	if (req->query_at < SIZE_MAX) {
 	if (req->query_at < SIZE_MAX) {
@@ -116,144 +149,118 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 	else if (num_query > MAX_QUERIES)
 	else if (num_query > MAX_QUERIES)
 		num_query = MAX_QUERIES;
 		num_query = MAX_QUERIES;
 
 
-	const size_t sz = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
-	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_shared(&req->pool, sz, cleanup_request);
-
-	if (query_ctx) {
-		// MAX_ID is a relatively small number, so allocate on the stack.
-		DEFINE_BITSET(bitset, MAX_ID);
-
-		initialize_single_query_context(req, on_multiple_query_result, &query_ctx->single);
-		memset(&query_ctx->single + 1,
-		       0,
-		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
-		query_ctx->num_query = num_query;
-		query_ctx->update_state = update_state;
-
-		size_t max_rand = MAX_ID - query_ctx->num_query + 1;
+	return num_query;
+}
 
 
-		for (size_t i = 0; i < query_ctx->num_query; i++) {
-			query_ctx->res[i].id = get_random_number(max_rand, &ctx->random_seed);
+static void initialize_ids(size_t num_query, query_result_t *res, unsigned int *seed)
+{
+	// MAX_ID is a relatively small number, so allocate on the stack.
+	DEFINE_BITSET(bitset, MAX_ID);
 
 
-			if (BITSET_ISSET(query_ctx->res[i].id, bitset))
-				query_ctx->res[i].id = max_rand - 1;
+	size_t max_rand = MAX_ID - num_query + 1;
 
 
-			BITSET_SET(query_ctx->res[i].id++, bitset);
-			max_rand++;
-		}
+	for (size_t i = 0; i < num_query; i++) {
+		res[i].id = get_random_number(max_rand, seed);
 
 
-		query_ctx->single.id = htonl(query_ctx->res->id);
+		if (BITSET_ISSET(res[i].id, bitset))
+			res[i].id = max_rand - 1;
 
 
-		if (execute_query(ctx, &query_ctx->single.param))
-			send_service_unavailable_error(DB_REQ_ERROR, req);
-		else
-			// Create a JSON generator while the query is processed.
-			query_ctx->gen = get_json_generator(&req->pool);
+		BITSET_SET(res[i].id++, bitset);
+		max_rand++;
 	}
 	}
-	else
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-
-	return 0;
 }
 }
 
 
-static void initialize_single_query_context(h2o_req_t *req,
-                                            on_result_t on_result,
-                                            single_query_ctx_t *query_ctx)
+static void on_multiple_query_error(db_query_param_t *param, const char *error_string)
 {
 {
-	memset(query_ctx, 0, sizeof(*query_ctx));
-	query_ctx->id_format = 1;
-	query_ctx->id_len = sizeof(query_ctx->id);
-	query_ctx->id_pointer = (const char *) &query_ctx->id;
-	query_ctx->param.command = WORLD_TABLE_NAME;
-	query_ctx->param.nParams = 1;
-	query_ctx->param.on_error = on_database_error;
-	query_ctx->param.on_result = on_result;
-	query_ctx->param.on_timeout = on_database_timeout;
-	query_ctx->param.paramFormats = &query_ctx->id_format;
-	query_ctx->param.paramLengths = &query_ctx->id_len;
-	query_ctx->param.paramValues = &query_ctx->id_pointer;
-	query_ctx->param.flags = IS_PREPARED;
-	query_ctx->param.resultFormat = 1;
-	query_ctx->req = req;
-}
+	const query_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(query_param_t,
+	                                                                 param,
+	                                                                 param);
+	multiple_query_ctx_t * const query_ctx = query_param->ctx;
 
 
-static void on_database_error(db_query_param_t *param, const char *error_string)
-{
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              param,
-	                                                              param);
-
-	send_error(BAD_GATEWAY, error_string, query_ctx->req);
-}
-
-static void on_database_timeout(db_query_param_t *param)
-{
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              param,
-	                                                              param);
+	if (query_ctx->gen) {
+		yajl_gen_free(query_ctx->gen);
+		query_ctx->gen = NULL;
+		send_error(BAD_GATEWAY, error_string, query_ctx->req);
+	}
 
 
-	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
+	query_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(query_ctx);
 }
 }
 
 
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result)
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result)
 {
 {
-	multiple_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(multiple_query_ctx_t,
-	                                                                single.param,
-	                                                                param);
+	query_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(query_param_t,
+	                                                           param,
+	                                                           param);
+	multiple_query_ctx_t * const query_ctx = query_param->ctx;
 
 
-	if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+	if (query_ctx->gen && PQresultStatus(result) == PGRES_TUPLES_OK) {
 		thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 		thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 		                                                      event_loop.h2o_ctx,
 		                                                      event_loop.h2o_ctx,
-		                                                      query_ctx->single.req->conn->ctx);
-		uint32_t * const random_number = &query_ctx->res[query_ctx->num_result++].random_number;
+		                                                      query_ctx->req->conn->ctx);
 
 
-		assert(PQnfields(result) == 2);
-		assert(PQntuples(result) == 1);
-		assert(PQgetlength(result, 0, 1) == sizeof(*random_number));
-		// Use memcpy() in case the result is not aligned.
-		memcpy(random_number, PQgetvalue(result, 0, 1), sizeof(*random_number));
-		*random_number = ntohl(*random_number);
-		PQclear(result);
+		process_result(result, query_ctx->res + query_ctx->num_result);
+		query_ctx->num_query_in_progress--;
+		query_ctx->num_result++;
+
+		const size_t num_query_remaining = query_ctx->num_query - query_ctx->num_result;
+
+		if (query_ctx->num_query_in_progress < num_query_remaining) {
+			const size_t idx = query_ctx->num_result + query_ctx->num_query_in_progress;
 
 
-		if (query_ctx->num_result < query_ctx->num_query) {
-			query_ctx->single.id = htonl(query_ctx->res[query_ctx->num_result].id);
+			query_param->id = htonl(query_ctx->res[idx].id);
 
 
-			if (!execute_query(ctx, &query_ctx->single.param))
+			if (!execute_query(ctx, &query_param->param)) {
+				query_ctx->num_query_in_progress++;
+				PQclear(result);
 				return DONE;
 				return DONE;
+			}
 
 
-			send_service_unavailable_error(DB_REQ_ERROR, query_ctx->single.req);
+			yajl_gen_free(query_ctx->gen);
+			query_ctx->gen = NULL;
+			send_service_unavailable_error(DB_REQ_ERROR, query_ctx->req);
 		}
 		}
-		else if (query_ctx->update_state == NO_UPDATE) {
+		else if (query_ctx->num_result == query_ctx->num_query)
 			serialize_items(query_ctx->res,
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
 			                query_ctx->num_result,
 			                query_ctx->gen,
 			                query_ctx->gen,
-			                query_ctx->single.req);
-			return DONE;
-		}
-		else {
-			query_ctx->single.param.command = UPDATE_QUERY;
-			query_ctx->single.param.nParams = 0;
-			query_ctx->single.param.on_result = on_update_result;
-			query_ctx->single.param.on_write_ready = on_update_write_ready;
-			query_ctx->single.param.paramFormats = NULL;
-			query_ctx->single.param.paramLengths = NULL;
-			query_ctx->single.param.paramValues = NULL;
-			query_ctx->single.param.flags = 0;
-
-			if (!execute_query(ctx, &query_ctx->single.param))
-				return DONE;
+			                query_ctx->req);
 
 
-			send_service_unavailable_error(DB_REQ_ERROR, query_ctx->single.req);
-		}
-	}
-	else {
-		send_error(BAD_GATEWAY, PQresultErrorMessage(result), query_ctx->single.req);
-		PQclear(result);
+		h2o_mem_release_shared(query_ctx);
 	}
 	}
+	else
+		on_multiple_query_error(param, PQresultErrorMessage(result));
 
 
+	PQclear(result);
 	return DONE;
 	return DONE;
 }
 }
 
 
+static void on_multiple_query_timeout(db_query_param_t *param)
+{
+	const query_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(query_param_t,
+	                                                                 param,
+	                                                                 param);
+	multiple_query_ctx_t * const query_ctx = query_param->ctx;
+
+	if (query_ctx->gen) {
+		yajl_gen_free(query_ctx->gen);
+		query_ctx->gen = NULL;
+		send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
+	}
+
+	query_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(query_ctx);
+}
+
+static void on_single_query_error(db_query_param_t *param, const char *error_string)
+{
+	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
+	                                                              param,
+	                                                              param);
+
+	send_error(BAD_GATEWAY, error_string, query_ctx->req);
+}
+
 static result_return_t on_single_query_result(db_query_param_t *param, PGresult *result)
 static result_return_t on_single_query_result(db_query_param_t *param, PGresult *result)
 {
 {
 	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
 	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
@@ -294,114 +301,151 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 	return DONE;
 	return DONE;
 }
 }
 
 
-static result_return_t on_update_result(db_query_param_t *param, PGresult *result)
+static void on_single_query_timeout(db_query_param_t *param)
 {
 {
-	multiple_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(multiple_query_ctx_t,
-	                                                                single.param,
-	                                                                param);
-	result_return_t ret = SUCCESS;
-	const ExecStatusType status = PQresultStatus(result);
-
-	switch (query_ctx->update_state) {
-		case CREATE:
-		case COPY_2:
-			if (status != PGRES_COMMAND_OK)
-				goto error;
-
-			query_ctx->update_state++;
-			break;
-		case COPY_1:
-			if (status != PGRES_COPY_IN)
-				goto error;
-
-			ret = WANT_WRITE;
-			break;
-		case UPDATE:
-			if (status != PGRES_COMMAND_OK)
-				goto error;
-
-			serialize_items(query_ctx->res,
-			                query_ctx->num_result,
-			                query_ctx->gen,
-			                query_ctx->single.req);
-			ret = DONE;
-			break;
-		default:
-			goto error;
-	}
+	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
+	                                                              param,
+	                                                              param);
 
 
-	PQclear(result);
-	return ret;
-error:
-	send_error(BAD_GATEWAY, PQresultErrorMessage(result), query_ctx->single.req);
-	PQclear(result);
-	return DONE;
+	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 }
 }
 
 
-static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn)
+static void on_update_error(db_query_param_t *param, const char *error_string)
 {
 {
-	multiple_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(multiple_query_ctx_t,
-	                                                                single.param,
-	                                                                param);
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_ctx,
-	                                                      query_ctx->single.req->conn->ctx);
-
-	if (query_ctx->num_result == query_ctx->num_query && query_ctx->update_state != COPY_2) {
-		const int rc = PQputCopyData(db_conn, H2O_STRLIT(COPY_HEADER));
-
-		if (!rc)
-			return 1;
-		else if (rc < 0)
-			return rc;
-
-		query_ctx->num_result = 0;
-		query_ctx->update_state = COPY_2;
+	const update_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(update_param_t,
+	                                                                  param,
+	                                                                  param);
+	update_ctx_t * const update_ctx = query_param->ctx;
+
+	if (update_ctx->gen) {
+		yajl_gen_free(update_ctx->gen);
+		update_ctx->gen = NULL;
+		send_error(BAD_GATEWAY, error_string, update_ctx->req);
 	}
 	}
 
 
-	if (query_ctx->num_result < query_ctx->num_query) {
-		assert(query_ctx->num_query - query_ctx->num_result <= MAX_QUERIES);
-
-		// There are at most MAX_QUERIES elements, so allocate on the stack.
-		struct __attribute__ ((__packed__)) {
-			uint16_t field_count;
-			uint32_t id_size;
-			uint32_t id;
-			uint32_t random_number_size;
-			uint32_t random_number;
-		} data[query_ctx->num_query - query_ctx->num_result];
-
-		memset(&data, 0, sizeof(data));
-
-		for (size_t i = 0; i < query_ctx->num_query; i++) {
-			query_ctx->res[i].random_number = get_random_number(MAX_ID, &ctx->random_seed) + 1;
-			data[i].field_count = htons(2);
-			data[i].id_size = htonl(sizeof(data->id));
-			data[i].id = htonl(query_ctx->res[i].id);
-			data[i].random_number_size = htonl(sizeof(data->random_number));
-			data[i].random_number = htonl(query_ctx->res[i].random_number);
-		}
+	update_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(update_ctx);
+}
 
 
-		const int rc = PQputCopyData(db_conn, (const char *) &data, sizeof(data));
+static result_return_t on_update_result(db_query_param_t *param, PGresult *result)
+{
+	update_param_t * const update_param = H2O_STRUCT_FROM_MEMBER(update_param_t,
+	                                                             param,
+	                                                             param);
+	update_ctx_t * const update_ctx = update_param->ctx;
+	result_return_t ret = DONE;
+
+	if (update_ctx->gen) {
+		if (update_param->update) {
+			if (PQresultStatus(result) == PGRES_COMMAND_OK) {
+				thread_context_t * const ctx =
+					H2O_STRUCT_FROM_MEMBER(thread_context_t,
+					                       event_loop.h2o_ctx,
+					                       update_ctx->req->conn->ctx);
+				const size_t num_query_remaining =
+					update_ctx->num_query - update_ctx->num_result;
+
+				update_ctx->num_query_in_progress--;
+
+				if (update_ctx->num_query_in_progress < num_query_remaining) {
+					const size_t idx = update_ctx->num_result +
+					                   update_ctx->num_query_in_progress;
+
+					update_param->random_number =
+						get_random_number(MAX_ID, &ctx->random_seed) + 1;
+					update_param->update = false;
+					snprintf(update_param->command,
+					         MAX_UPDATE_QUERY_LEN,
+					         UPDATE_QUERY,
+					         update_ctx->res[idx].id,
+					         update_ctx->res[idx].id,
+					         update_param->random_number);
+
+					if (!execute_query(ctx, &update_param->param)) {
+						update_ctx->num_query_in_progress++;
+						PQclear(result);
+						return DONE;
+					}
+
+					yajl_gen_free(update_ctx->gen);
+					update_ctx->gen = NULL;
+					send_service_unavailable_error(DB_REQ_ERROR,
+					                               update_ctx->req);
+				}
+				else if (update_ctx->num_result == update_ctx->num_query)
+					serialize_items(update_ctx->res,
+					                update_ctx->num_result,
+					                update_ctx->gen,
+					                update_ctx->req);
+
+				h2o_mem_release_shared(update_ctx);
+			}
+			else
+				on_update_error(param, PQresultErrorMessage(result));
+		}
+		else if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+			process_result(result, update_ctx->res + update_ctx->num_result);
+			update_ctx->res[update_ctx->num_result].random_number =
+				update_param->random_number;
+			update_ctx->num_result++;
+			update_param->update = true;
+			ret = SUCCESS;
+		}
+		else
+			on_update_error(param, PQresultErrorMessage(result));
+	}
+	else {
+		update_ctx->num_query_in_progress--;
+		h2o_mem_release_shared(update_ctx);
+	}
 
 
-		if (!rc)
-			return 1;
-		else if (rc < 0)
-			return rc;
+	PQclear(result);
+	return ret;
+}
 
 
-		query_ctx->num_result = query_ctx->num_query;
+static void on_update_timeout(db_query_param_t *param)
+{
+	const update_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(update_param_t,
+	                                                                  param,
+	                                                                  param);
+	update_ctx_t * const update_ctx = query_param->ctx;
+
+	if (update_ctx->gen) {
+		yajl_gen_free(update_ctx->gen);
+		update_ctx->gen = NULL;
+		send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, update_ctx->req);
 	}
 	}
 
 
-	if (query_ctx->num_result == query_ctx->num_query) {
-		const int rc = PQputCopyEnd(db_conn, NULL);
+	update_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(update_ctx);
+}
 
 
-		if (!rc)
-			return 1;
-		else if (rc < 0)
-			return rc;
+static void process_result(PGresult *result, query_result_t *out)
+{
+	assert(PQnfields(result) == 2);
+	assert(PQntuples(result) == 1);
+
+	const char * const id = PQgetvalue(result, 0, 0);
+	const char * const random_number = PQgetvalue(result, 0, 1);
+
+	if (PQfformat(result, 0)) {
+		assert(PQgetlength(result, 0, 0) == sizeof(out->id));
+		// Use memcpy() in case the result is not aligned; the reason we are
+		// copying over the id is because the results may arrive in any order.
+		memcpy(&out->id, id, sizeof(out->id));
+		out->id = ntohl(out->id);
 	}
 	}
+	else
+		out->id = atoi(id);
 
 
-	return PQflush(db_conn);
+	if (PQfformat(result, 1)) {
+		assert(PQgetlength(result, 0, 1) == sizeof(out->random_number));
+		// Use memcpy() in case the result is not aligned.
+		memcpy(&out->random_number, random_number, sizeof(out->random_number));
+		out->random_number = ntohl(out->random_number);
+	}
+	else
+		out->random_number = atoi(random_number);
 }
 }
 
 
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen)
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen)
@@ -422,22 +466,16 @@ static void serialize_items(const query_result_t *res,
                             yajl_gen gen,
                             yajl_gen gen,
                             h2o_req_t *req)
                             h2o_req_t *req)
 {
 {
-	// In principle the JSON generator can be created here, but we do it earlier,
-	// so that it happens in parallel with the database query; we assume that the
-	// allocation will rarely fail, so that the delayed error handling is not
-	// problematic.
-	if (gen) {
-		CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
+	CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
 
 
-		for (size_t i = 0; i < num_result; i++)
-			if (serialize_item(res[i].id, res[i].random_number, gen))
-				goto error_yajl;
+	for (size_t i = 0; i < num_result; i++)
+		if (serialize_item(res[i].id, res[i].random_number, gen))
+			goto error_yajl;
 
 
-		CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
+	CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
 
 
-		if (!send_json_response(gen, false, req))
-			return;
-	}
+	if (!send_json_response(gen, false, req))
+		return;
 
 
 error_yajl:
 error_yajl:
 	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
@@ -447,7 +485,73 @@ int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 {
 	IGNORE_FUNCTION_PARAMETER(self);
 	IGNORE_FUNCTION_PARAMETER(self);
 
 
-	return do_multiple_queries(NO_UPDATE, req);
+	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
+	                                                      event_loop.h2o_ctx,
+	                                                      req->conn->ctx);
+
+	const size_t num_query = get_query_number(req);
+	size_t base_size = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
+
+	base_size = ((base_size + _Alignof(query_param_t) - 1) / _Alignof(query_param_t));
+	base_size = base_size * _Alignof(query_param_t);
+
+	const size_t num_query_in_progress = MIN(num_query, ctx->config->max_db_conn_num);
+	const size_t sz = base_size + num_query_in_progress * sizeof(query_param_t);
+
+	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_shared(&req->pool,
+	                                                              sz,
+	                                                              cleanup_multiple_query);
+
+	if (query_ctx) {
+		memset(query_ctx, 0, sz);
+		query_ctx->num_query = num_query;
+		query_ctx->num_query_in_progress = num_query_in_progress;
+		query_ctx->req = req;
+		query_ctx->query_param = (query_param_t *) ((char *) query_ctx + base_size);
+		initialize_ids(num_query, query_ctx->res, &ctx->random_seed);
+
+		for (size_t i = 0; i < query_ctx->num_query_in_progress; i++) {
+			query_ctx->query_param[i].ctx = query_ctx;
+			// We need a copy of id because the original may be overwritten
+			// by a completed query.
+			query_ctx->query_param[i].id = htonl(query_ctx->res[i].id);
+			query_ctx->query_param[i].id_format = 1;
+			query_ctx->query_param[i].id_len = sizeof(query_ctx->query_param[i].id);
+			query_ctx->query_param[i].id_pointer =
+				(const char *) &query_ctx->query_param[i].id;
+			query_ctx->query_param[i].param.command = WORLD_TABLE_NAME;
+			query_ctx->query_param[i].param.nParams = 1;
+			query_ctx->query_param[i].param.on_error = on_multiple_query_error;
+			query_ctx->query_param[i].param.on_result = on_multiple_query_result;
+			query_ctx->query_param[i].param.on_timeout = on_multiple_query_timeout;
+			query_ctx->query_param[i].param.paramFormats =
+				&query_ctx->query_param[i].id_format;
+			query_ctx->query_param[i].param.paramLengths =
+				&query_ctx->query_param[i].id_len;
+			query_ctx->query_param[i].param.paramValues =
+				&query_ctx->query_param[i].id_pointer;
+			query_ctx->query_param[i].param.flags = IS_PREPARED;
+			query_ctx->query_param[i].param.resultFormat = 1;
+
+			if (execute_query(ctx, &query_ctx->query_param[i].param)) {
+				query_ctx->num_query_in_progress = i;
+				send_service_unavailable_error(DB_REQ_ERROR, req);
+				return 0;
+			}
+
+			h2o_mem_addref_shared(query_ctx);
+		}
+
+		// Create a JSON generator while the queries are processed.
+		query_ctx->gen = get_json_generator(&req->pool);
+
+		if (!query_ctx->gen)
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+
+	return 0;
 }
 }
 
 
 int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
@@ -460,8 +564,22 @@ int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_ctx));
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_ctx));
 
 
 	if (query_ctx) {
 	if (query_ctx) {
-		initialize_single_query_context(req, on_single_query_result, query_ctx);
+		memset(query_ctx, 0, sizeof(*query_ctx));
 		query_ctx->id = htonl(get_random_number(MAX_ID, &ctx->random_seed) + 1);
 		query_ctx->id = htonl(get_random_number(MAX_ID, &ctx->random_seed) + 1);
+		query_ctx->id_format = 1;
+		query_ctx->id_len = sizeof(query_ctx->id);
+		query_ctx->id_pointer = (const char *) &query_ctx->id;
+		query_ctx->param.command = WORLD_TABLE_NAME;
+		query_ctx->param.nParams = 1;
+		query_ctx->param.on_error = on_single_query_error;
+		query_ctx->param.on_result = on_single_query_result;
+		query_ctx->param.on_timeout = on_single_query_timeout;
+		query_ctx->param.paramFormats = &query_ctx->id_format;
+		query_ctx->param.paramLengths = &query_ctx->id_len;
+		query_ctx->param.paramValues = &query_ctx->id_pointer;
+		query_ctx->param.flags = IS_PREPARED;
+		query_ctx->param.resultFormat = 1;
+		query_ctx->req = req;
 
 
 		if (execute_query(ctx, &query_ctx->param))
 		if (execute_query(ctx, &query_ctx->param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
 			send_service_unavailable_error(DB_REQ_ERROR, req);
@@ -476,5 +594,67 @@ int updates(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 {
 	IGNORE_FUNCTION_PARAMETER(self);
 	IGNORE_FUNCTION_PARAMETER(self);
 
 
-	return do_multiple_queries(CREATE, req);
+	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
+	                                                      event_loop.h2o_ctx,
+	                                                      req->conn->ctx);
+
+	const size_t num_query = get_query_number(req);
+	size_t base_size = offsetof(update_ctx_t, res) + num_query * sizeof(query_result_t);
+
+	base_size = ((base_size + _Alignof(update_param_t) - 1) / _Alignof(update_param_t));
+	base_size = base_size * _Alignof(update_param_t);
+
+	const size_t num_query_in_progress = MIN(num_query, ctx->config->max_db_conn_num);
+	const size_t struct_size = base_size + num_query_in_progress * sizeof(update_param_t);
+	const size_t sz = struct_size + num_query_in_progress * MAX_UPDATE_QUERY_LEN;
+	update_ctx_t * const update_ctx = h2o_mem_alloc_shared(&req->pool, sz, cleanup_update);
+
+	if (update_ctx) {
+		memset(update_ctx, 0, struct_size);
+		update_ctx->num_query = num_query;
+		update_ctx->num_query_in_progress = num_query_in_progress;
+		update_ctx->req = req;
+		update_ctx->update_param = (update_param_t *) ((char *) update_ctx + base_size);
+		initialize_ids(num_query, update_ctx->res, &ctx->random_seed);
+
+		char *command = (char *) update_ctx + struct_size;
+
+		for (size_t i = 0; i < update_ctx->num_query_in_progress; i++) {
+			update_ctx->update_param[i].command = command;
+			command += MAX_UPDATE_QUERY_LEN;
+			update_ctx->update_param[i].ctx = update_ctx;
+			update_ctx->update_param[i].param.command =
+				update_ctx->update_param[i].command;
+			update_ctx->update_param[i].param.on_error = on_update_error;
+			update_ctx->update_param[i].param.on_result = on_update_result;
+			update_ctx->update_param[i].param.on_timeout = on_update_timeout;
+			update_ctx->update_param[i].param.resultFormat = 1;
+			update_ctx->update_param[i].random_number =
+				get_random_number(MAX_ID, &ctx->random_seed) + 1;
+			snprintf(update_ctx->update_param[i].command,
+			         MAX_UPDATE_QUERY_LEN,
+			         UPDATE_QUERY,
+			         update_ctx->res[i].id,
+			         update_ctx->res[i].id,
+			         update_ctx->update_param[i].random_number);
+
+			if (execute_query(ctx, &update_ctx->update_param[i].param)) {
+				update_ctx->num_query_in_progress = i;
+				send_service_unavailable_error(DB_REQ_ERROR, req);
+				return 0;
+			}
+
+			h2o_mem_addref_shared(update_ctx);
+		}
+
+		// Create a JSON generator while the queries are processed.
+		update_ctx->gen = get_json_generator(&req->pool);
+
+		if (!update_ctx->gen)
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+
+	return 0;
 }
 }

+ 7 - 8
frameworks/CSharp/revenj/Revenj.Bench/RestService.cs

@@ -1,7 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
-using System.ServiceModel.Web;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
 using FrameworkBench;
 using FrameworkBench;
@@ -10,8 +9,8 @@ using Revenj.DatabasePersistence;
 using Revenj.DomainPatterns;
 using Revenj.DomainPatterns;
 using Revenj.Extensibility;
 using Revenj.Extensibility;
 using Revenj.Http;
 using Revenj.Http;
-using Revenj.Utility;
 using Revenj.Serialization;
 using Revenj.Serialization;
+using Revenj.Utility;
 
 
 namespace Revenj.Bench
 namespace Revenj.Bench
 {
 {
@@ -34,20 +33,20 @@ namespace Revenj.Bench
 			this.Context = new ThreadLocal<Context>(() => new Context(factory, queryManager));
 			this.Context = new ThreadLocal<Context>(() => new Context(factory, queryManager));
 		}
 		}
 
 
-		[WebGet(UriTemplate = "/plaintext")]
+		[Route(HTTP.GET, "/plaintext", false)]
 		public Stream PlainText(IResponseContext response)
 		public Stream PlainText(IResponseContext response)
 		{
 		{
 			response.ContentType = "text/plain";
 			response.ContentType = "text/plain";
 			return HelloWorld;
 			return HelloWorld;
 		}
 		}
 
 
-		[WebGet(UriTemplate = "/json")]
+		[Route(HTTP.GET, "/json", false)]
 		public Message JSON()
 		public Message JSON()
 		{
 		{
 			return new Message { message = "Hello, World!" };
 			return new Message { message = "Hello, World!" };
 		}
 		}
 
 
-		[WebGet(UriTemplate = "/db")]
+		[Route(HTTP.GET, "/db")]
 		public World SingleQuery()
 		public World SingleQuery()
 		{
 		{
 			var ctx = Context.Value;
 			var ctx = Context.Value;
@@ -56,7 +55,7 @@ namespace Revenj.Bench
 		}
 		}
 
 
 		//while IList<World> would work, it would fall back to IEnumerable<IJsonObject> which would create garbage
 		//while IList<World> would work, it would fall back to IEnumerable<IJsonObject> which would create garbage
-		[WebGet(UriTemplate = "/queries/{count}")]
+		[Route(HTTP.GET, "/queries/{count}")]
 		public IList<IJsonObject> MultipleQueries(string count, IResponseContext response)
 		public IList<IJsonObject> MultipleQueries(string count, IResponseContext response)
 		{
 		{
 			int repeat;
 			int repeat;
@@ -70,7 +69,7 @@ namespace Revenj.Bench
 
 
 		private static readonly Comparison<World> ASC = (l, r) => l.id - r.id;
 		private static readonly Comparison<World> ASC = (l, r) => l.id - r.id;
 
 
-		[WebGet(UriTemplate = "/updates/{count}")]
+		[Route(HTTP.GET, "/updates/{count}")]
 		public World[] Updates(string count)
 		public World[] Updates(string count)
 		{
 		{
 			int repeat;
 			int repeat;
@@ -89,7 +88,7 @@ namespace Revenj.Bench
 
 
 		private static readonly Comparison<KeyValuePair<int, string>> Comparison = (l, r) => string.Compare(l.Value, r.Value, StringComparison.Ordinal);
 		private static readonly Comparison<KeyValuePair<int, string>> Comparison = (l, r) => string.Compare(l.Value, r.Value, StringComparison.Ordinal);
 
 
-		[WebGet(UriTemplate = "/fortunes")]
+		[Route(HTTP.GET, "/fortunes")]
 		public Fortunes Fortunes()
 		public Fortunes Fortunes()
 		{
 		{
 			var ctx = Context.Value;
 			var ctx = Context.Value;

+ 2 - 2
frameworks/Elixir/phoenix/benchmark_config.json

@@ -11,7 +11,7 @@
             "plaintext_url": "/plaintext",
             "plaintext_url": "/plaintext",
             "port": 8080,
             "port": 8080,
             "approach": "Realistic",
             "approach": "Realistic",
-            "classification": "Micro",
+            "classification": "Fullstack",
             "database": "Postgres",
             "database": "Postgres",
             "framework": "Phoenix",
             "framework": "Phoenix",
             "language": "Elixir",
             "language": "Elixir",
@@ -23,7 +23,7 @@
             "database_os": "Linux",
             "database_os": "Linux",
             "display_name": "Phoenix",
             "display_name": "Phoenix",
             "notes": "",
             "notes": "",
-            "versus": ""
+            "versus": "Cowboy"
         }
         }
     }]
     }]
 }
 }

+ 4 - 4
frameworks/Java/grizzly-bm/pom.xml

@@ -29,8 +29,8 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>2.3.2</version>
                 <version>2.3.2</version>
                 <configuration>
                 <configuration>
-                    <source>1.7</source>
-                    <target>1.7</target>
+                    <source>1.8</source>
+                    <target>1.8</target>
                     <optimize>true</optimize>
                     <optimize>true</optimize>
                     <debug>false</debug>
                     <debug>false</debug>
                 </configuration>
                 </configuration>
@@ -68,13 +68,13 @@
         <dependency>
         <dependency>
             <groupId>org.glassfish.grizzly</groupId>
             <groupId>org.glassfish.grizzly</groupId>
             <artifactId>grizzly-http-server</artifactId>
             <artifactId>grizzly-http-server</artifactId>
-            <version>2.3.11</version>
+            <version>2.3.28</version>
             <!--<version>3.0-SNAPSHOT</version>-->
             <!--<version>3.0-SNAPSHOT</version>-->
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
             <artifactId>jackson-databind</artifactId>
-            <version>2.3.0</version>
+            <version>2.8.5</version>
         </dependency>
         </dependency>
     </dependencies>
     </dependencies>
     <!-- <repositories>
     <!-- <repositories>

+ 4 - 4
frameworks/Java/grizzly-jersey/pom.xml

@@ -14,10 +14,10 @@
   </prerequisites>
   </prerequisites>
 
 
   <properties>
   <properties>
-    <grizzly.version>2.3.23</grizzly.version>
+    <grizzly.version>2.3.28</grizzly.version>
     <jersey.version>1.19</jersey.version>
     <jersey.version>1.19</jersey.version>
     <jersey-mustache.version>1.0.0</jersey-mustache.version>
     <jersey-mustache.version>1.0.0</jersey-mustache.version>
-    <jackson.version>2.7.0</jackson.version>
+    <jackson.version>2.8.5</jackson.version>
     <mustache.version>0.9.1</mustache.version>
     <mustache.version>0.9.1</mustache.version>
     <hibernate.version>4.3.11.Final</hibernate.version>
     <hibernate.version>4.3.11.Final</hibernate.version>
     <mysql-connector.version>5.1.38</mysql-connector.version>
     <mysql-connector.version>5.1.38</mysql-connector.version>
@@ -103,8 +103,8 @@
         <artifactId>maven-compiler-plugin</artifactId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.1</version>
         <version>3.1</version>
         <configuration>
         <configuration>
-          <source>1.7</source>
-          <target>1.7</target>
+          <source>1.8</source>
+          <target>1.8</target>
           <optimize>true</optimize>
           <optimize>true</optimize>
           <debug>false</debug>
           <debug>false</debug>
         </configuration>
         </configuration>

+ 23 - 5
frameworks/Java/rapidoid/benchmark_config.json

@@ -3,7 +3,7 @@
   "tests": [
   "tests": [
     {
     {
       "default": {
       "default": {
-        "setup_file": "setup-high-level",
+        "setup_file": "setup-default",
         "json_url": "/json",
         "json_url": "/json",
         "plaintext_url": "/plaintext",
         "plaintext_url": "/plaintext",
         "port": 8080,
         "port": 8080,
@@ -22,8 +22,8 @@
         "versus": ""
         "versus": ""
       },
       },
       "mysql": {
       "mysql": {
-        "setup_file": "setup-high-level",
-        "fortune_url": "/fortunes/mysql",
+        "setup_file": "setup-mysql",
+        "fortune_url": "/fortunes",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Platform",
         "classification": "Platform",
@@ -35,12 +35,30 @@
         "webserver": "None",
         "webserver": "None",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "rapidoid",
+        "display_name": "rapidoid-mysql",
+        "notes": "",
+        "versus": ""
+      },
+      "postgres": {
+        "setup_file": "setup-postgres",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "rapidoid",
+        "language": "Java",
+        "orm": "micro",
+        "platform": "Rapidoid",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "rapidoid-postgres",
         "notes": "",
         "notes": "",
         "versus": ""
         "versus": ""
       },
       },
       "http-fast": {
       "http-fast": {
-        "setup_file": "setup-low-level",
+        "setup_file": "setup-http-fast",
         "json_url": "/json",
         "json_url": "/json",
         "plaintext_url": "/plaintext",
         "plaintext_url": "/plaintext",
         "port": 8080,
         "port": 8080,

+ 7 - 1
frameworks/Java/rapidoid/pom.xml

@@ -16,7 +16,12 @@
 		<dependency>
 		<dependency>
 			<groupId>org.rapidoid</groupId>
 			<groupId>org.rapidoid</groupId>
 			<artifactId>rapidoid-quick</artifactId>
 			<artifactId>rapidoid-quick</artifactId>
-			<version>5.2.2</version>
+			<version>5.2.6</version>
+		</dependency>
+		<dependency>
+			<groupId>org.postgresql</groupId>
+			<artifactId>postgresql</artifactId>
+			<version>9.4.1211</version>
 		</dependency>
 		</dependency>
 	</dependencies>
 	</dependencies>
 
 
@@ -38,6 +43,7 @@
 
 
 			<plugin>
 			<plugin>
 				<artifactId>maven-assembly-plugin</artifactId>
 				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.6</version>
 				<configuration>
 				<configuration>
 					<descriptorRefs>
 					<descriptorRefs>
 						<descriptorRef>jar-with-dependencies</descriptorRef>
 						<descriptorRef>jar-with-dependencies</descriptorRef>

+ 12 - 0
frameworks/Java/rapidoid/setup-default.sh

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+fw_depends java maven
+
+mvn clean compile assembly:single
+
+cd target
+
+java -server \
+  -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp \
+  rapidoid-1.0-jar-with-dependencies.jar \
+  highlevel.Main profiles=production dbhost="$DBHOST" &

+ 0 - 8
frameworks/Java/rapidoid/setup-high-level.sh

@@ -1,8 +0,0 @@
-#!/bin/bash
-
-fw_depends java maven
-
-mvn clean compile assembly:single
-
-cd target
-java -server -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp rapidoid-1.0-jar-with-dependencies.jar highlevel.Main dbhost="$DBHOST" &

+ 12 - 0
frameworks/Java/rapidoid/setup-http-fast.sh

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+fw_depends java maven
+
+mvn clean compile assembly:single
+
+cd target
+
+java -server \
+  -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp \
+  rapidoid-1.0-jar-with-dependencies.jar \
+  lowlevel.Main profiles=production dbhost="$DBHOST" &

+ 0 - 8
frameworks/Java/rapidoid/setup-low-level.sh

@@ -1,8 +0,0 @@
-#!/bin/bash
-
-fw_depends java maven
-
-mvn clean compile assembly:single
-
-cd target
-java -server -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp rapidoid-1.0-jar-with-dependencies.jar lowlevel.Main &

+ 12 - 0
frameworks/Java/rapidoid/setup-mysql.sh

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+fw_depends java maven
+
+mvn clean compile assembly:single
+
+cd target
+
+java -server \
+  -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp \
+  rapidoid-1.0-jar-with-dependencies.jar \
+  highlevel.Main profiles=mysql,production dbhost="$DBHOST" &

+ 12 - 0
frameworks/Java/rapidoid/setup-postgres.sh

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+fw_depends java maven
+
+mvn clean compile assembly:single
+
+cd target
+
+java -server \
+  -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp \
+  rapidoid-1.0-jar-with-dependencies.jar \
+  highlevel.Main profiles=postgres,production dbhost="$DBHOST" &

+ 11 - 4
frameworks/Java/rapidoid/src/main/java/common/Helper.java

@@ -2,11 +2,18 @@ package common;
 
 
 public class Helper {
 public class Helper {
 
 
-	// this configuration was copied from the undertow-example project
-	public static final String MYSQL_CONFIG = "jdbcCompliantTruncation=false" +
+	// most of this configuration was copied from the other projects
+	public static final String MYSQL_CONFIG = "useSSL=false&jdbcCompliantTruncation=false" +
 		"&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true" +
 		"&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true" +
 		"&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048" +
 		"&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048" +
-		"&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useUnbufferedInput=false&useReadAheadInput=false" +
-		"&maintainTimeStats=false&useServerPrepStmts&cacheRSMetadata=true";
+		"&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useServerPrepStmts=true&enableQueryTimeouts=false" +
+		"&useUnbufferedIO=false&useReadAheadInput=false&maintainTimeStats=false&cacheRSMetadata=true";
+
+	// most of this configuration was copied from the other projects
+	public static final String POSTGRES_CONFIG = "jdbcCompliantTruncation=false" +
+		"&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true" +
+		"&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048" +
+		"&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useServerPrepStmts=true&enableQueryTimeouts=false" +
+		"&useUnbufferedIO=false&useReadAheadInput=false&maintainTimeStats=false&cacheRSMetadata=true";
 
 
 }
 }

+ 32 - 13
frameworks/Java/rapidoid/src/main/java/highlevel/Main.java

@@ -2,6 +2,7 @@ package highlevel;
 
 
 import common.Helper;
 import common.Helper;
 import common.Message;
 import common.Message;
+import org.rapidoid.commons.Env;
 import org.rapidoid.config.Conf;
 import org.rapidoid.config.Conf;
 import org.rapidoid.http.MediaType;
 import org.rapidoid.http.MediaType;
 import org.rapidoid.log.Log;
 import org.rapidoid.log.Log;
@@ -14,31 +15,49 @@ public class Main {
 
 
 	public static void main(String[] args) {
 	public static void main(String[] args) {
 
 
-		App.args(args,
-			"production",
-			"c3p0.maxPoolSize=256",
-			"c3p0.maxIdleTimeExcessConnections=256",
-			"c3p0.maxStatementsPerConnection=3",
-			"http.serverName=X",
-			"http.mandatoryHeaders.connection=false",
-			"http.timeout=0");
+		App.args(args);
 
 
-		String dbHost = Conf.ROOT.entry("dbhost").or("localhost");
-		Log.info("Database hostname is: " + dbHost);
+		Conf.C3P0.set("maxPoolSize", 256);
+		Conf.C3P0.set("maxIdleTimeExcessConnections", 256);
+		Conf.C3P0.set("maxStatementsPerConnection", 3);
+
+		Conf.HTTP.set("maxPipeline", 128);
+		Conf.HTTP.set("timeout", 0);
+		Conf.HTTP.sub("mandatoryHeaders").set("connection", false);
 
 
 		On.port(8080);
 		On.port(8080);
 
 
-		On.get("/plaintext").managed(false).contentType(MediaType.TEXT_PLAIN).serve("Hello, world!");
+		if (Env.hasAnyProfile("mysql", "postgres")) {
+			setupDbHandlers();
+		} else {
+			setupSimpleHandlers();
+		}
+	}
 
 
+	private static void setupSimpleHandlers() {
+		On.get("/plaintext").managed(false).contentType(MediaType.TEXT_PLAIN).serve("Hello, world!");
 		On.get("/json").managed(false).json(() -> new Message("Hello, world!"));
 		On.get("/json").managed(false).json(() -> new Message("Hello, world!"));
+	}
+
+	private static void setupDbHandlers() {
+		String dbHost = Conf.ROOT.entry("dbhost").or("localhost");
+		Log.info("Database hostname is: " + dbHost);
+
+		String dbUrl;
+
+		if (Env.hasProfile("mysql")) {
+			dbUrl = "jdbc:mysql://" + dbHost + ":3306/hello_world?" + Helper.MYSQL_CONFIG;
+		} else {
+			dbUrl = "jdbc:postgresql://" + dbHost + ":5432/hello_world?" + Helper.POSTGRES_CONFIG;
+		}
 
 
 		JdbcClient mysqlJdbc = JDBC.newApi()
 		JdbcClient mysqlJdbc = JDBC.newApi()
-			.url("jdbc:mysql://" + dbHost + ":3306/hello_world?" + Helper.MYSQL_CONFIG)
+			.url(dbUrl)
 			.username("benchmarkdbuser")
 			.username("benchmarkdbuser")
 			.password("benchmarkdbpass")
 			.password("benchmarkdbpass")
 			.pooled();
 			.pooled();
 
 
-		On.get("/fortunes/mysql").html(new FortunesHandler(mysqlJdbc));
+		On.get("/fortunes").html(new FortunesHandler(mysqlJdbc));
 	}
 	}
 
 
 }
 }

+ 8 - 0
frameworks/Java/rapidoid/src/main/java/lowlevel/Main.java

@@ -1,8 +1,16 @@
 package lowlevel;
 package lowlevel;
 
 
+import org.rapidoid.config.Conf;
+import org.rapidoid.setup.App;
+
 public class Main {
 public class Main {
 
 
 	public static void main(String[] args) throws Exception {
 	public static void main(String[] args) throws Exception {
+		App.args(args);
+
+		Conf.HTTP.set("maxPipeline", 128);
+		Conf.HTTP.set("timeout", 0);
+
 		new PlaintextAndJsonServer().listen(8080);
 		new PlaintextAndJsonServer().listen(8080);
 	}
 	}
 
 

+ 7 - 9
frameworks/Java/rapidoid/src/main/java/lowlevel/PlaintextAndJsonServer.java

@@ -2,12 +2,11 @@ package lowlevel;
 
 
 import common.Message;
 import common.Message;
 import org.rapidoid.buffer.Buf;
 import org.rapidoid.buffer.Buf;
-import org.rapidoid.data.BufRange;
-import org.rapidoid.data.BufRanges;
 import org.rapidoid.http.AbstractHttpServer;
 import org.rapidoid.http.AbstractHttpServer;
 import org.rapidoid.http.HttpStatus;
 import org.rapidoid.http.HttpStatus;
 import org.rapidoid.http.MediaType;
 import org.rapidoid.http.MediaType;
 import org.rapidoid.net.abstracts.Channel;
 import org.rapidoid.net.abstracts.Channel;
+import org.rapidoid.net.impl.RapidoidHelper;
 
 
 public class PlaintextAndJsonServer extends AbstractHttpServer {
 public class PlaintextAndJsonServer extends AbstractHttpServer {
 
 
@@ -22,15 +21,14 @@ public class PlaintextAndJsonServer extends AbstractHttpServer {
 	}
 	}
 
 
 	@Override
 	@Override
-	protected HttpStatus handle(Channel ctx, Buf buf, BufRange verb, BufRange uri, BufRange path, BufRange query,
-	                            BufRange protocol, BufRanges headers, boolean isGet, boolean isKeepAlive, BufRange body) {
+	protected HttpStatus handle(Channel ctx, Buf buf, RapidoidHelper data) {
 
 
-		if (isGet) {
-			if (matches(buf, path, URI_PLAINTEXT)) {
-				return ok(ctx, isKeepAlive, HELLO_WORLD, MediaType.TEXT_PLAIN);
+		if (data.isGet.value) {
+			if (matches(buf, data.path, URI_PLAINTEXT)) {
+				return ok(ctx, data.isKeepAlive.value, HELLO_WORLD, MediaType.TEXT_PLAIN);
 
 
-			} else if (matches(buf, path, URI_JSON)) {
-				return serializeToJson(ctx, isKeepAlive, new Message("Hello, World!"));
+			} else if (matches(buf, data.path, URI_JSON)) {
+				return serializeToJson(ctx, data.isKeepAlive.value, new Message("Hello, World!"));
 			}
 			}
 		}
 		}
 
 

+ 7 - 12
frameworks/Java/wicket/README.md

@@ -3,31 +3,26 @@
 This is the wicket portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 This is the wicket portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
 
 ### JSON Encoding Test
 ### JSON Encoding Test
-* [JSON test source](src/main/java/hellowicket/HelloJsonResponse.java)
+* [JSON test source](src/main/java/hellowicket/HelloJsonResponse.java) - uses Jackson + Afterburner module
 
 
 ### Data-Store/Database Mapping Test
 ### Data-Store/Database Mapping Test
-Use [Hibernate](http://www.hibernate.org/) for interaction with MySQL database.
+Use JDBC for interaction with MySQL database.
 
 
 * [Database test source](src/main/java/hellowicket/HelloDbResponse.java)
 * [Database test source](src/main/java/hellowicket/HelloDbResponse.java)
 * [Database Entity](src/main/java/hellowicket/World.java)
 * [Database Entity](src/main/java/hellowicket/World.java)
-* [Hibernate Configuration](src/main/resources/hibernate.cfg.xml)
 
 
 ## Versions
 ## Versions
 
 
-* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
-* [Wicket 6.12.0](http://wicket.apache.org/)
-* [Hibernate 4.1.1](http://www.hibernate.org/)
-* [MySQL 5.5.29](http://dev.mysql.com/)
-* [Maven 2.2.1](https://maven.apache.org/)
-* [Resin 4.0.34](http://www.caucho.com/)
-* [Jackson 2.3.0](http://wiki.fasterxml.com/JacksonHome)
+* [Wicket 7.5.0](http://wicket.apache.org/)
+* [MySQL 5.5.+](http://dev.mysql.com/)
+* [Jackson 2.8.5](http://wiki.fasterxml.com/JacksonHome)
 
 
 ## Test URLs
 ## Test URLs
 
 
 ### JSON Encoding Test
 ### JSON Encoding Test
 
 
-    http://localhost:8080/wicket/hello_json
+    http://localhost:8080/wicket/json
 
 
 ### Database Mapping Test
 ### Database Mapping Test
 
 
-    http://localhost:8080/wicket/hello_db?queries=5
+    http://localhost:8080/wicket/db?queries=5

+ 13 - 32
frameworks/Java/wicket/pom.xml

@@ -4,7 +4,7 @@
 	<groupId>hellowicket</groupId>
 	<groupId>hellowicket</groupId>
 	<artifactId>hellowicket</artifactId>
 	<artifactId>hellowicket</artifactId>
 	<packaging>war</packaging>
 	<packaging>war</packaging>
-	<version>1.0-SNAPSHOT</version>
+	<version>1.0</version>
 	<name>Hello Wicket</name>
 	<name>Hello Wicket</name>
 	<description>Wicket project for the TechEmpower Benchmark</description>
 	<description>Wicket project for the TechEmpower Benchmark</description>
 	<organization>
 	<organization>
@@ -20,9 +20,8 @@
 	</licenses>
 	</licenses>
 	<properties>
 	<properties>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<wicket.version>7.1.0</wicket.version>
-		<jetty.version>8.1.18.v20150929</jetty.version>
-		<slf4j.version>1.7.12</slf4j.version>
+		<wicket.version>7.5.0</wicket.version>
+		<slf4j.version>1.7.21</slf4j.version>
 	</properties>
 	</properties>
 	<dependencies>
 	<dependencies>
 		<!-- WICKET DEPENDENCIES -->
 		<!-- WICKET DEPENDENCIES -->
@@ -36,7 +35,7 @@
 		<dependency>
 		<dependency>
 			<groupId>com.zaxxer</groupId>
 			<groupId>com.zaxxer</groupId>
 			<artifactId>HikariCP</artifactId>
 			<artifactId>HikariCP</artifactId>
-			<version>2.4.3</version>
+			<version>2.5.1</version>
 			<scope>compile</scope>
 			<scope>compile</scope>
 		</dependency>
 		</dependency>
 
 
@@ -60,24 +59,22 @@
 			<scope>test</scope>
 			<scope>test</scope>
 		</dependency>
 		</dependency>
 
 
-		<!-- JETTY DEPENDENCIES FOR TESTING -->
 		<dependency>
 		<dependency>
-			<groupId>org.eclipse.jetty.aggregate</groupId>
-			<artifactId>jetty-all-server</artifactId>
-			<version>${jetty.version}</version>
-			<scope>provided</scope>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-afterburner</artifactId>
+			<version>2.8.5</version>
 		</dependency>
 		</dependency>
 
 
 		<dependency>
 		<dependency>
 			<groupId>com.fasterxml.jackson.core</groupId>
 			<groupId>com.fasterxml.jackson.core</groupId>
-			<artifactId>jackson-databind</artifactId>
-			<version>2.7.0</version>
+			<artifactId>jackson-annotations</artifactId>
+			<version>2.8.5</version>
 		</dependency>
 		</dependency>
 
 
 		<dependency>
 		<dependency>
 			<groupId>mysql</groupId>
 			<groupId>mysql</groupId>
 			<artifactId>mysql-connector-java</artifactId>
 			<artifactId>mysql-connector-java</artifactId>
-			<version>5.1.38</version>
+			<version>6.0.5</version>
 		</dependency>
 		</dependency>
 	</dependencies>
 	</dependencies>
 	<build>
 	<build>
@@ -118,30 +115,14 @@
 				<inherited>true</inherited>
 				<inherited>true</inherited>
 				<groupId>org.apache.maven.plugins</groupId>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>
 				<artifactId>maven-compiler-plugin</artifactId>
-				<version>3.1</version>
+				<version>3.6.0</version>
 				<configuration>
 				<configuration>
-					<source>1.7</source>
-					<target>1.7</target>
+					<source>1.8</source>
+					<target>1.8</target>
 					<optimize>true</optimize>
 					<optimize>true</optimize>
 					<debug>false</debug>
 					<debug>false</debug>
 				</configuration>
 				</configuration>
 			</plugin>
 			</plugin>
-			<plugin>
-				<groupId>org.mortbay.jetty</groupId>
-				<artifactId>jetty-maven-plugin</artifactId>
-				<version>${jetty.version}</version>
-				<configuration>
-					<connectors>
-						<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
-							<port>8080</port>
-							<maxIdleTime>3600000</maxIdleTime>
-						</connector>
-						<!--connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector"> 
-							<port>8443</port> <maxIdleTime>3600000</maxIdleTime> <keystore>${project.build.directory}/test-classes/keystore</keystore> 
-							<password>wicket</password> <keyPassword>wicket</keyPassword> </connector -->
-					</connectors>
-				</configuration>
-			</plugin>
 		</plugins>
 		</plugins>
 	</build>
 	</build>
 
 

+ 1 - 2
frameworks/Java/wicket/setup.sh

@@ -2,10 +2,9 @@
 
 
 fw_depends java resin maven
 fw_depends java resin maven
 
 
-sed -i 's|127.0.0.1|'${DBHOST}'|g' src/main/webapp/WEB-INF/resin-web.xml
 sed -i 's|localhost|'${DBHOST}'|g' src/main/java/hellowicket/WicketApplication.java
 sed -i 's|localhost|'${DBHOST}'|g' src/main/java/hellowicket/WicketApplication.java
 
 
 mvn clean compile war:war
 mvn clean compile war:war
 rm -rf $RESIN_HOME/webapps/*
 rm -rf $RESIN_HOME/webapps/*
-cp target/hellowicket-1.0-SNAPSHOT.war $RESIN_HOME/webapps/wicket.war
+cp target/hellowicket-*.war $RESIN_HOME/webapps/wicket.war
 resinctl start
 resinctl start

+ 0 - 8
frameworks/Java/wicket/src/main/java/hellowicket/BasePage.html

@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<html xmlns:wicket="http://wicket.apache.org">
-  <head>
-  </head>
-  <body>
-    <wicket:child />
-  </body>
-</html>

+ 0 - 8
frameworks/Java/wicket/src/main/java/hellowicket/BasePage.java

@@ -1,8 +0,0 @@
-package hellowicket;
-
-import org.apache.wicket.markup.html.WebPage;
-
-public class BasePage extends WebPage
-{
-  private static final long serialVersionUID = 1L;
-}

+ 21 - 19
frameworks/Java/wicket/src/main/java/hellowicket/HelloDbResponse.java

@@ -1,27 +1,23 @@
 package hellowicket;
 package hellowicket;
 
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.string.StringValue;
+
+import javax.sql.DataSource;
 import java.sql.Connection;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLException;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
-import javax.sql.DataSource;
-
-import org.apache.wicket.request.resource.AbstractResource;
-import org.apache.wicket.util.string.StringValue;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 public class HelloDbResponse extends AbstractResource
 public class HelloDbResponse extends AbstractResource
 {
 {
   private static final long serialVersionUID = 1L;
   private static final long serialVersionUID = 1L;
 
 
   private static final int DB_ROWS = 10000;
   private static final int DB_ROWS = 10000;
-
-  private static final String CONTENT_TYPE = "application/json";
-  private static final ObjectMapper mapper = new ObjectMapper();
+  private static final String TEXT_PLAIN = "text/plain";
 
 
   protected ResourceResponse newResourceResponse(Attributes attributes)
   protected ResourceResponse newResourceResponse(Attributes attributes)
   {
   {
@@ -38,29 +34,35 @@ public class HelloDbResponse extends AbstractResource
     final int queries = qs;
     final int queries = qs;
 
 
     final ResourceResponse response = new ResourceResponse();
     final ResourceResponse response = new ResourceResponse();
-    response.setContentType(CONTENT_TYPE);
 
 
     try
     try
     {
     {
-      final String data = getDataFromDatabase(queriesParam, queries);
+      final byte[] data = getDataFromDatabase(queriesParam, queries);
+      final WebResponse webResponse = (WebResponse) attributes.getResponse();
+      webResponse.setContentLength(data.length);
+      webResponse.setContentType(HelloJsonResponse.APPLICATION_JSON);
       response.setWriteCallback(new WriteCallback()
       response.setWriteCallback(new WriteCallback()
       {
       {
         public void writeData(Attributes attributes)
         public void writeData(Attributes attributes)
         {
         {
-          attributes.getResponse().write(data);
+          webResponse.write(data);
         }
         }
       });
       });
     }
     }
     catch (Exception ex)
     catch (Exception ex)
     {
     {
-      response.setContentType("text/plain");
+      response.setContentType(TEXT_PLAIN);
       response.setError(500, ex.getClass().getSimpleName() + ": " + ex.getMessage());
       response.setError(500, ex.getClass().getSimpleName() + ": " + ex.getMessage());
       ex.printStackTrace();
       ex.printStackTrace();
     }
     }
     return response;
     return response;
   }
   }
 
 
-  private String getDataFromDatabase(final StringValue queriesParam, final int queries)
+  @Override
+  protected void setResponseHeaders(final ResourceResponse resourceResponse, final Attributes attributes) {
+  }
+
+  private byte[] getDataFromDatabase(final StringValue queriesParam, final int queries)
       throws SQLException, JsonProcessingException
       throws SQLException, JsonProcessingException
   {
   {
     final ThreadLocalRandom random = ThreadLocalRandom.current();
     final ThreadLocalRandom random = ThreadLocalRandom.current();
@@ -83,16 +85,16 @@ public class HelloDbResponse extends AbstractResource
       }
       }
     }
     }
 
 
-    String data;
+    byte[] data;
     if (queriesParam.isNull())
     if (queriesParam.isNull())
     {
     {
       // request to /db should return JSON object
       // request to /db should return JSON object
-      data = HelloDbResponse.mapper.writeValueAsString(worlds[0]);
+      data = HelloJsonResponse.MAPPER.writeValueAsBytes(worlds[0]);
     }
     }
     else
     else
     {
     {
       // request to /db?queries=xyz should return JSON array (issue #648)
       // request to /db?queries=xyz should return JSON array (issue #648)
-      data = HelloDbResponse.mapper.writeValueAsString(worlds);
+      data = HelloJsonResponse.MAPPER.writeValueAsBytes(worlds);
     }
     }
     return data;
     return data;
   }
   }

+ 22 - 14
frameworks/Java/wicket/src/main/java/hellowicket/HelloJsonResponse.java

@@ -1,36 +1,39 @@
 package hellowicket;
 package hellowicket;
 
 
-import java.io.IOException;
-import java.util.Map;
-
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
+import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.resource.AbstractResource;
 import org.apache.wicket.request.resource.AbstractResource;
-import org.apache.wicket.util.collections.MiniMap;
 
 
-import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
 
 
 public class HelloJsonResponse extends AbstractResource
 public class HelloJsonResponse extends AbstractResource
 {
 {
   private static final long serialVersionUID = 1L;
   private static final long serialVersionUID = 1L;
 
 
-  private static final String CONTENT_TYPE = "application/json";
-  private static final ObjectMapper mapper = new ObjectMapper();
+  private static final String HELLO_WORLD = "Hello, World!";
+  public static final String APPLICATION_JSON = "application/json";
+  public static final ObjectMapper MAPPER = new ObjectMapper();
+
+  static  {
+    MAPPER.registerModule(new AfterburnerModule());
+  }
 
 
   protected ResourceResponse newResourceResponse(Attributes attributes)
   protected ResourceResponse newResourceResponse(Attributes attributes)
   {
   {
     final ResourceResponse response = new ResourceResponse();
     final ResourceResponse response = new ResourceResponse();
-    response.setContentLength(27);
-    response.setContentType(CONTENT_TYPE);
     response.setWriteCallback(new WriteCallback()
     response.setWriteCallback(new WriteCallback()
     {
     {
       public void writeData(Attributes attributes)
       public void writeData(Attributes attributes)
       {
       {
-        Map<String, String> data = new MiniMap<>(1);
-        data.put("message", "Hello, World!");
-
         try
         try
         {
         {
-            String json = HelloJsonResponse.mapper.writeValueAsString(data);
-            attributes.getResponse().write(json);
+          final WebResponse webResponse = (WebResponse) attributes.getResponse();
+          webResponse.setContentLength(27);
+          webResponse.setContentType(APPLICATION_JSON);
+          JsonMessage message = new JsonMessage(HELLO_WORLD);
+          byte[] json = MAPPER.writeValueAsBytes(message);
+          webResponse.write(json);
         }
         }
         catch (IOException ex)
         catch (IOException ex)
         {
         {
@@ -40,4 +43,9 @@ public class HelloJsonResponse extends AbstractResource
     });
     });
     return response;
     return response;
   }
   }
+
+  @Override
+  protected void setResponseHeaders(final ResourceResponse resourceResponse, final Attributes attributes) {
+
+  }
 }
 }

+ 15 - 0
frameworks/Java/wicket/src/main/java/hellowicket/JsonMessage.java

@@ -0,0 +1,15 @@
+package hellowicket;
+
+public class JsonMessage {
+
+	private final String message;
+
+	public JsonMessage(String message) {
+        super();
+		this.message = message;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+}

+ 50 - 0
frameworks/Java/wicket/src/main/java/hellowicket/RequestMapper.java

@@ -0,0 +1,50 @@
+package hellowicket;
+
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.IRequestMapper;
+import org.apache.wicket.request.Request;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+
+import hellowicket.dbupdates.HelloDbUpdatesReference;
+import hellowicket.fortune.FortunePage;
+import hellowicket.plaintext.HelloTextReference;
+
+/**
+ * Custom request mapper optimized for the application needs
+ */
+public class RequestMapper implements IRequestMapper {
+
+    private static final Url FORTUNES_URL = Url.parse("fortunes");
+
+    @Override
+    public IRequestHandler mapRequest(final Request request) {
+        final String url = request.getUrl().getPath();
+        switch (url) {
+            case "json":
+                return new ResourceReferenceRequestHandler(new HelloJsonReference());
+            case "db":
+                return new ResourceReferenceRequestHandler(new HelloDbReference());
+            case "updates":
+                return new ResourceReferenceRequestHandler(new HelloDbUpdatesReference());
+            case "plaintext":
+                return new ResourceReferenceRequestHandler(new HelloTextReference());
+            case "fortunes":
+                return new RenderPageRequestHandler(new PageProvider(FortunePage.class));
+            default:
+        }
+        return null;
+    }
+
+    @Override
+    public int getCompatibilityScore(final Request request) {
+        return 0;
+    }
+
+    @Override
+    public Url mapHandler(final IRequestHandler requestHandler) {
+        return FORTUNES_URL;
+    }
+}

+ 7 - 20
frameworks/Java/wicket/src/main/java/hellowicket/WicketApplication.java

@@ -1,16 +1,11 @@
 package hellowicket;
 package hellowicket;
 
 
-import javax.naming.InitialContext;
-import javax.sql.DataSource;
-
+import com.zaxxer.hikari.HikariDataSource;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.settings.RequestCycleSettings;
 import org.apache.wicket.settings.RequestCycleSettings;
 
 
-import com.zaxxer.hikari.HikariDataSource;
-
-import hellowicket.dbupdates.HelloDbUpdatesReference;
-import hellowicket.fortune.FortunePage;
-import hellowicket.plaintext.HelloTextReference;
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
 
 
 /**
 /**
  * Application object for your web application..
  * Application object for your web application..
@@ -32,14 +27,6 @@ public class WicketApplication extends WebApplication
 
 
 		db = newDataSource();
 		db = newDataSource();
 
 
-		// mount the resources under test
-		mountResource("/json", new HelloJsonReference());
-		mountResource("/db", new HelloDbReference());
-		mountResource("/updates", new HelloDbUpdatesReference());
-		mountResource("/plaintext", new HelloTextReference());
-
-		mountPage("/fortunes", FortunePage.class);
-
 		// disable response caching to be more close to other
 		// disable response caching to be more close to other
 		// test applications' behavior
 		// test applications' behavior
 		RequestCycleSettings requestCycleSettings = getRequestCycleSettings();
 		RequestCycleSettings requestCycleSettings = getRequestCycleSettings();
@@ -47,6 +34,8 @@ public class WicketApplication extends WebApplication
 
 
 		// set UTF-8 for /fortunes test
 		// set UTF-8 for /fortunes test
 		requestCycleSettings.setResponseRequestEncoding("UTF-8");
 		requestCycleSettings.setResponseRequestEncoding("UTF-8");
+
+		setRootRequestMapper(new RequestMapper());
 	}
 	}
 
 
 	@Override
 	@Override
@@ -78,8 +67,6 @@ public class WicketApplication extends WebApplication
 		DataSource dataSource;
 		DataSource dataSource;
 		try
 		try
 		{
 		{
-			Class.forName("com.mysql.jdbc.Driver");
-
 			if (useResinDataSource())
 			if (useResinDataSource())
 			{
 			{
 				InitialContext jndiContext = new InitialContext();
 				InitialContext jndiContext = new InitialContext();
@@ -92,8 +79,8 @@ public class WicketApplication extends WebApplication
 				HikariDataSource ds = new HikariDataSource();
 				HikariDataSource ds = new HikariDataSource();
 
 
 				// use faster DataSource impl
 				// use faster DataSource impl
-				ds.setJdbcUrl("jdbc:mysql://localhost:3306/hello_world?jdbcCompliantTruncation=false&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useUnbufferedInput=false&useReadAheadInput=false&maintainTimeStats=false&useServerPrepStmts&cacheRSMetadata=true");
-				ds.setDriverClassName("com.mysql.jdbc.Driver");
+				ds.setJdbcUrl("jdbc:mysql://localhost:3306/hello_world?jdbcCompliantTruncation=false&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useUnbufferedInput=false&useReadAheadInput=false&maintainTimeStats=false&useServerPrepStmts=true&cacheRSMetadata=true&useSSL=false");
+				ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
 				ds.setUsername("benchmarkdbuser");
 				ds.setUsername("benchmarkdbuser");
 				ds.setPassword("benchmarkdbpass");
 				ds.setPassword("benchmarkdbpass");
 				dataSource = ds;
 				dataSource = ds;

+ 14 - 11
frameworks/Java/wicket/src/main/java/hellowicket/dbupdates/HelloDbUpdatesResource.java

@@ -1,16 +1,15 @@
 package hellowicket.dbupdates;
 package hellowicket.dbupdates;
 
 
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.resource.AbstractResource;
+
+import javax.sql.DataSource;
 import java.sql.Connection;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSet;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
-import javax.sql.DataSource;
-
-import org.apache.wicket.request.resource.AbstractResource;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
+import hellowicket.HelloJsonResponse;
 import hellowicket.WicketApplication;
 import hellowicket.WicketApplication;
 import hellowicket.World;
 import hellowicket.World;
 
 
@@ -24,8 +23,6 @@ public class HelloDbUpdatesResource extends AbstractResource
 
 
   private static final int DB_ROWS = 10000;
   private static final int DB_ROWS = 10000;
 
 
-  private static final ObjectMapper mapper = new ObjectMapper();
-
   protected ResourceResponse newResourceResponse(Attributes attributes)
   protected ResourceResponse newResourceResponse(Attributes attributes)
   {
   {
     int _queries = attributes.getRequest().getQueryParameters().getParameterValue("queries").toInt(1);
     int _queries = attributes.getRequest().getQueryParameters().getParameterValue("queries").toInt(1);
@@ -40,7 +37,6 @@ public class HelloDbUpdatesResource extends AbstractResource
     final int queries = _queries;
     final int queries = _queries;
 
 
     final ResourceResponse response = new ResourceResponse();
     final ResourceResponse response = new ResourceResponse();
-    response.setContentType("application/json");
 
 
     response.setWriteCallback(new WriteCallback() {
     response.setWriteCallback(new WriteCallback() {
       public void writeData(Attributes attributes)
       public void writeData(Attributes attributes)
@@ -78,8 +74,11 @@ public class HelloDbUpdatesResource extends AbstractResource
             }
             }
           }
           }
 
 
-          String data = HelloDbUpdatesResource.mapper.writeValueAsString(worlds);
-          attributes.getResponse().write(data);
+          byte[] data = HelloJsonResponse.MAPPER.writeValueAsBytes(worlds);
+          final WebResponse webResponse = (WebResponse) attributes.getResponse();
+          webResponse.setContentLength(data.length);
+          webResponse.setContentType(HelloJsonResponse.APPLICATION_JSON);
+          webResponse.write(data);
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
@@ -89,4 +88,8 @@ public class HelloDbUpdatesResource extends AbstractResource
     });
     });
     return response;
     return response;
   }
   }
+
+  @Override
+  protected void setResponseHeaders(final ResourceResponse resourceResponse, final Attributes attributes) {
+  }
 }
 }

+ 12 - 1
frameworks/Java/wicket/src/main/java/hellowicket/fortune/FortunePage.java

@@ -14,6 +14,7 @@ import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.request.http.WebResponse;
 
 
 /**
 /**
  * A page that loads all fortune cookies. This mimics the Servlet example
  * A page that loads all fortune cookies. This mimics the Servlet example
@@ -22,6 +23,7 @@ import org.apache.wicket.markup.html.list.ListView;
  */
  */
 public class FortunePage extends WebPage {
 public class FortunePage extends WebPage {
 	private static final long serialVersionUID = 1L;
 	private static final long serialVersionUID = 1L;
+	private static final String TEXT_HTML = "text/html";
 
 
 	public FortunePage() throws Exception {
 	public FortunePage() throws Exception {
 		List<Fortune> fortunes = new ArrayList<>(10000);
 		List<Fortune> fortunes = new ArrayList<>(10000);
@@ -31,7 +33,7 @@ public class FortunePage extends WebPage {
 				Connection connection = dataSource.getConnection();
 				Connection connection = dataSource.getConnection();
 				PreparedStatement statement = connection.prepareStatement("SELECT id, message FROM Fortune",
 				PreparedStatement statement = connection.prepareStatement("SELECT id, message FROM Fortune",
 						ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
 						ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
-				ResultSet resultSet = statement.executeQuery();) {
+				ResultSet resultSet = statement.executeQuery()) {
 
 
 			while (resultSet.next()) {
 			while (resultSet.next()) {
 				fortunes.add(new Fortune(resultSet.getInt("id"), resultSet.getString("message")));
 				fortunes.add(new Fortune(resultSet.getInt("id"), resultSet.getString("message")));
@@ -54,4 +56,13 @@ public class FortunePage extends WebPage {
 		};
 		};
 		add(listView);
 		add(listView);
 	}
 	}
+
+	@Override
+	protected void configureResponse(final WebResponse response) {
+		response.setContentType(TEXT_HTML);
+	}
+
+	@Override
+	protected void renderXmlDecl() {
+	}
 }
 }

+ 11 - 5
frameworks/Java/wicket/src/main/java/hellowicket/plaintext/HelloTextResource.java

@@ -1,9 +1,10 @@
 package hellowicket.plaintext;
 package hellowicket.plaintext;
 
 
-import java.nio.charset.Charset;
-
+import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.resource.AbstractResource;
 import org.apache.wicket.request.resource.AbstractResource;
 
 
+import java.nio.charset.Charset;
+
 /**
 /**
  * A resource that implements the requirements for
  * A resource that implements the requirements for
  * <a href="http://www.techempower.com/benchmarks/#section=code">Test type 6: Plaintext</a>
  * <a href="http://www.techempower.com/benchmarks/#section=code">Test type 6: Plaintext</a>
@@ -18,14 +19,19 @@ public class HelloTextResource extends AbstractResource
   protected ResourceResponse newResourceResponse(Attributes attributes)
   protected ResourceResponse newResourceResponse(Attributes attributes)
   {
   {
     ResourceResponse response = new ResourceResponse();
     ResourceResponse response = new ResourceResponse();
-    response.setContentType(CONTENT_TYPE);
-    response.setContentLength(DATA.length);
     response.setWriteCallback(new WriteCallback() {
     response.setWriteCallback(new WriteCallback() {
       public void writeData(Attributes attributes)
       public void writeData(Attributes attributes)
       {
       {
-        attributes.getResponse().write(DATA);
+        final WebResponse webResponse = (WebResponse) attributes.getResponse();
+        webResponse.setContentType(CONTENT_TYPE);
+        webResponse.setContentLength(DATA.length);
+        webResponse.write(DATA);
       }
       }
     });
     });
     return response;
     return response;
   }
   }
+
+  @Override
+  protected void setResponseHeaders(final ResourceResponse resourceResponse, final Attributes attributes) {
+  }
 }
 }

+ 0 - 14
frameworks/Java/wicket/src/main/webapp/WEB-INF/resin-web.xml

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

+ 1 - 1
frameworks/Python/bottle/requirements.txt

@@ -2,7 +2,7 @@ bottle==0.12.9
 bottle-sqlalchemy==0.4.3
 bottle-sqlalchemy==0.4.3
 SQLAlchemy==1.0.12
 SQLAlchemy==1.0.12
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 mysqlclient==1.3.7
 mysqlclient==1.3.7
 greenlet==0.4.9
 greenlet==0.4.9
 uwsgi==2.0.12
 uwsgi==2.0.12

+ 1 - 1
frameworks/Python/django/requirements.txt

@@ -5,6 +5,6 @@ psycopg2==2.6.1
 ujson==1.35
 ujson==1.35
 
 
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 
 
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/falcon/requirements.txt

@@ -1,5 +1,5 @@
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 Cython==0.23.4
 Cython==0.23.4
 falcon==0.3.0
 falcon==0.3.0
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/flask/requirements.txt

@@ -5,6 +5,6 @@ SQLAlchemy==1.0.12
 Flask-SQLAlchemy==2.1
 Flask-SQLAlchemy==2.1
 mysqlclient==1.3.7
 mysqlclient==1.3.7
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 uwsgi==2.0.12
 uwsgi==2.0.12
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/pyramid/requirements.txt

@@ -1,6 +1,6 @@
 psycopg2==2.6.1
 psycopg2==2.6.1
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 SQLAlchemy==1.0.12
 SQLAlchemy==1.0.12
 pyramid==1.6.1
 pyramid==1.6.1
 pyramid_chameleon==0.3
 pyramid_chameleon==0.3

+ 1 - 1
frameworks/Python/turbogears/requirements.txt

@@ -6,5 +6,5 @@ mysqlclient==1.3.7
 jinja2==2.8
 jinja2==2.8
 
 
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/web2py/requirements.txt

@@ -1,4 +1,4 @@
 mysqlclient==1.3.7
 mysqlclient==1.3.7
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/weppy/requirements.txt

@@ -1,6 +1,6 @@
 psycopg2==2.6.1
 psycopg2==2.6.1
 weppy==0.8.4
 weppy==0.8.4
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 uwsgi==2.0.12
 uwsgi==2.0.12
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/wheezyweb/requirements.txt

@@ -5,6 +5,6 @@ SQLAlchemy==1.0.12
 mysqlclient==1.3.7
 mysqlclient==1.3.7
 
 
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 
 
 greenlet==0.4.9
 greenlet==0.4.9

+ 1 - 1
frameworks/Python/wsgi/requirements.txt

@@ -1,4 +1,4 @@
 ujson==1.35
 ujson==1.35
 gunicorn==19.4.5
 gunicorn==19.4.5
-meinheld==0.5.9
+meinheld==0.6.1
 greenlet==0.4.9
 greenlet==0.4.9

+ 2 - 2
frameworks/Scala/akka-http/build.sbt

@@ -9,8 +9,8 @@ scalaVersion := "2.11.8"
 resolvers += "Akka Snapshot Repository" at "http://repo.akka.io/snapshots/"
 resolvers += "Akka Snapshot Repository" at "http://repo.akka.io/snapshots/"
 
 
 libraryDependencies ++= Seq(
 libraryDependencies ++= Seq(
-  "com.typesafe.akka" %% "akka-http-experimental" % "2.4.9",
-  "com.typesafe.akka" %% "akka-http-spray-json-experimental" % "2.4.9",
+  "com.typesafe.akka" %% "akka-http" % "10.0.0",
+  "com.typesafe.akka" %% "akka-http-spray-json" % "10.0.0",
   "mysql" % "mysql-connector-java" % "5.1.38",
   "mysql" % "mysql-connector-java" % "5.1.38",
   "com.zaxxer" % "HikariCP" % "2.5.1",
   "com.zaxxer" % "HikariCP" % "2.5.1",
   "org.scalatra.scalate" %% "scalate-core" % "1.7.0",
   "org.scalatra.scalate" %% "scalate-core" % "1.7.0",

+ 3 - 0
frameworks/Scala/fintrospect/Dockerfile

@@ -0,0 +1,3 @@
+FROM mysql
+
+ADD create.sql /docker-entrypoint-initdb.d/

+ 7 - 7
frameworks/Scala/fintrospect/README.md

@@ -4,16 +4,16 @@
 The tests were run with:
 The tests were run with:
 
 
 * [Java Oracle 1.8.0_25](http://www.oracle.com/technetwork/java/javase)
 * [Java Oracle 1.8.0_25](http://www.oracle.com/technetwork/java/javase)
-* [fintrospect 13.5.2](https://github.com/daviddenton/fintrospect)
-* [finagle 6.36.0](https://github.com/twitter/finagle)
+* [fintrospect 13.13.0](https://github.com/daviddenton/fintrospect)
 
 
 ## Test URLs
 ## Test URLs
 
 
-### JSON Encoding Test
-http://localhost:9000/json
-
-### Plaintext Test
-http://localhost:9000/plaintext
+- JSON Encoding: http://localhost:9000/json
+- Plaintext: http://localhost:9000/plaintext
+- Fortunes: http://localhost:9000/fortunes
+- Single Query: http://localhost:9000/db
+- Multi Query: http://localhost:9000/queries?queries=1
+- Update Query: http://localhost:9000/updates?queries=1
 
 
 ## How to run
 ## How to run
 sbt 'oneJar'
 sbt 'oneJar'

+ 5 - 1
frameworks/Scala/fintrospect/benchmark_config.json

@@ -6,12 +6,16 @@
         "orm": "Raw",
         "orm": "Raw",
         "database_os": "Linux",
         "database_os": "Linux",
         "setup_file": "setup",
         "setup_file": "setup",
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
         "json_url": "/json",
         "json_url": "/json",
+        "query_url": "/queries?queries=",
         "plaintext_url": "/plaintext",
         "plaintext_url": "/plaintext",
+        "update_url": "/updates?queries=",
         "port": 9000,
         "port": 9000,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Micro",
         "classification": "Micro",
-        "database": "None",
+        "database": "MySQL",
         "framework": "fintrospect",
         "framework": "fintrospect",
         "language": "Scala",
         "language": "Scala",
         "platform": "Netty",
         "platform": "Netty",

+ 6 - 4
frameworks/Scala/fintrospect/build.sbt

@@ -15,8 +15,10 @@ com.github.retronym.SbtOneJar.oneJarSettings
 mainClass in(Compile, run) := Some("FintrospectBenchmarkServer")
 mainClass in(Compile, run) := Some("FintrospectBenchmarkServer")
 
 
 libraryDependencies ++= Seq(
 libraryDependencies ++= Seq(
-  "io.fintrospect" %% "fintrospect-core" % "13.11.0",
-  "io.fintrospect" %% "fintrospect-json4s" % "13.11.0"
-  )
+  "io.fintrospect" %% "fintrospect-core" % "13.13.0",
+  "io.fintrospect" %% "fintrospect-json4s" % "13.13.0",
+  "io.fintrospect" %% "fintrospect-mustache" % "13.13.0",
+  "com.twitter" %% "finagle-mysql" % "6.40.0"
+)
 
 
-resolvers += Resolver.sonatypeRepo("snapshots")
+resolvers += Resolver.sonatypeRepo("snapshots")

+ 11 - 0
frameworks/Scala/fintrospect/run_db.sh

@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+cp ../../../config/create.sql .
+
+docker build -t dbi .
+
+rm create.sql
+
+docker run --name db -d \
+  -e MYSQL_ROOT_PASSWORD=123 \
+  -p 3306:3306 dbi

+ 2 - 0
frameworks/Scala/fintrospect/setup.sh

@@ -2,6 +2,8 @@
 
 
 fw_depends java sbt
 fw_depends java sbt
 
 
+sbt clean
+
 sbt 'oneJar' -batch
 sbt 'oneJar' -batch
 
 
 java -jar target/scala-2.11/*fintrospect*one-jar.jar &
 java -jar target/scala-2.11/*fintrospect*one-jar.jar &

+ 5 - 0
frameworks/Scala/fintrospect/source_code

@@ -1,2 +1,7 @@
 fintrospect/src/main/scala/
 fintrospect/src/main/scala/
+fintrospect/src/main/scala/Database.scala
+fintrospect/src/main/scala/FortunesRoute.scala
+fintrospect/src/main/scala/JsonRoute.scala
+fintrospect/src/main/scala/PlainTextRoute.scala
+fintrospect/src/main/scala/DatabaseRoutes.scala
 fintrospect/src/main/scala/FintrospectBenchmarkServer.scala
 fintrospect/src/main/scala/FintrospectBenchmarkServer.scala

+ 22 - 0
frameworks/Scala/fintrospect/src/main/scala/Database.scala

@@ -0,0 +1,22 @@
+import com.twitter.finagle.Mysql
+import com.twitter.finagle.client.DefaultPool.Param
+import com.twitter.finagle.mysql.Client
+import com.twitter.finagle.stats.NullStatsReceiver
+import com.twitter.finagle.tracing.NullTracer
+import com.twitter.util.Duration.fromSeconds
+import com.twitter.util.NullMonitor
+import io.fintrospect.configuration.Host
+
+object Database {
+  def apply(dbHost: Host): Client = {
+    Mysql.client
+      .withCredentials("benchmarkdbuser", "benchmarkdbpass")
+      .withDatabase("hello_world")
+      .configured(Param(low = 0, high = 10, idleTime = fromSeconds(5 * 60), bufferSize = 0, maxWaiters = Int.MaxValue))
+      .withStatsReceiver(NullStatsReceiver)
+      .withMonitor(NullMonitor)
+      .withTracer(NullTracer)
+      .withMaxConcurrentPrepareStatements(256)
+      .newRichClient(dbHost.value + ":3306")
+  }
+}

+ 81 - 0
frameworks/Scala/fintrospect/src/main/scala/DatabaseRoutes.scala

@@ -0,0 +1,81 @@
+import com.twitter.finagle.Service
+import com.twitter.finagle.http.Method.Get
+import com.twitter.finagle.http.Status.{NotFound, Ok}
+import com.twitter.finagle.http.{Request, Response}
+import com.twitter.finagle.mysql.Parameter.wrap
+import com.twitter.finagle.mysql.{Client, IntValue, Result, ResultSet}
+import com.twitter.util.Future.collect
+import io.fintrospect.RouteSpec.RequestValidation
+import io.fintrospect.RouteSpec.RequestValidation.none
+import io.fintrospect.formats.Json4sJackson.JsonFormat.{array, number, obj}
+import io.fintrospect.formats.Json4sJackson.ResponseBuilder.implicits._
+import io.fintrospect.parameters.ParameterSpec.int
+import io.fintrospect.parameters.{ParameterSpec, Query}
+import io.fintrospect.{RouteSpec, ServerRoutes}
+import org.json4s.JValue
+
+import scala.language.reflectiveCalls
+import scala.util.{Random, Try}
+
+object DatabaseRoutes {
+
+  private val toJson: PartialFunction[Result, Option[JValue]] = {
+    case rs: ResultSet => rs.rows.headOption
+      .map(row => {
+        val IntValue(id) = row("id").get
+        val IntValue(random) = row("randomNumber").get
+        obj("id" -> number(id), "randomNumber" -> number(random))
+      })
+    case _ => None
+  }
+
+  private def generateRandomNumber = Random.nextInt(9999) + 1
+
+  def apply(database: Client) = {
+    val getStatement = database.prepare("SELECT id, randomNumber FROM world WHERE id = ?")
+    val updateStatement = database.prepare("UPDATE world SET randomNumber = ? WHERE id = ?")
+
+    val queryRoute = RouteSpec(validation = none).at(Get) / "db" bindTo Service.mk {
+      r: Request => getStatement(generateRandomNumber)
+        .map(toJson)
+        .map(_.map(Ok(_)).getOrElse(NotFound()).build())
+    }
+
+    val numberOfQueries = Query.optional(ParameterSpec.string("queries").map {
+      i => Try(i.toInt).getOrElse(1).max(1).min(500)
+    })
+
+    val multipleRoute = RouteSpec()
+      .taking(numberOfQueries)
+      .at(Get) / "queries" bindTo Service.mk {
+      r: Request => {
+        collect(1.to((numberOfQueries <-- r).getOrElse(1))
+          .map(i => getStatement(generateRandomNumber).map(toJson)))
+          .map(f => f.flatMap(_.toSeq))
+          .flatMap(c => Ok(array(c)))
+      }
+    }
+
+    val updateRoute = RouteSpec()
+      .taking(numberOfQueries)
+      .at(Get) / "updates" bindTo Service.mk {
+      r: Request => {
+        collect(1.to((numberOfQueries <-- r).getOrElse(1))
+          .map(i => {
+            val id = generateRandomNumber
+            updateStatement(generateRandomNumber, id)
+              .flatMap(_ => getStatement(id))
+              .map(toJson)
+          }))
+          .map(f => f.flatMap(_.toSeq))
+          .flatMap(c => Ok(array(c)))
+      }
+    }
+
+    new ServerRoutes[Request, Response] {
+      add(queryRoute)
+      add(multipleRoute)
+      add(updateRoute)
+    }
+  }
+}

+ 24 - 26
frameworks/Scala/fintrospect/src/main/scala/FintrospectBenchmarkServer.scala

@@ -1,40 +1,38 @@
-import java.time.ZonedDateTime._
-import java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME
+import java.util.TimeZone.getTimeZone
 
 
-import com.twitter.finagle.http.Method.Get
-import com.twitter.finagle.http.Request
-import com.twitter.finagle.http.Status._
 import com.twitter.finagle.http.path.Root
 import com.twitter.finagle.http.path.Root
+import com.twitter.finagle.http.{Request, Response}
 import com.twitter.finagle.stats.NullStatsReceiver
 import com.twitter.finagle.stats.NullStatsReceiver
 import com.twitter.finagle.tracing.NullTracer
 import com.twitter.finagle.tracing.NullTracer
-import com.twitter.finagle.{Http, Service}
-import com.twitter.io.Buf
+import com.twitter.finagle.{Filter, Http}
 import com.twitter.util.{Await, NullMonitor}
 import com.twitter.util.{Await, NullMonitor}
-import io.fintrospect.formats.Json4sJackson.JsonFormat._
-import io.fintrospect.{ModuleSpec, RouteSpec}
+import io.fintrospect.ModuleSpec
+import io.fintrospect.configuration.Host
+import io.fintrospect.renderers.simplejson.SimpleJson
+import org.apache.commons.lang.time.FastDateFormat.getInstance
+
+import scala.util.Properties
 
 
 object FintrospectBenchmarkServer extends App {
 object FintrospectBenchmarkServer extends App {
 
 
-  val preAllocatedHelloWorldText = Buf.Utf8("Hello, World!")
+  val dateFormat = getInstance("EEE, d MMM yyyy HH:mm:ss 'GMT'", getTimeZone("GMT"))
 
 
-  val plainTextHelloWorld = {
-    import io.fintrospect.formats.PlainText.ResponseBuilder.implicits._
-    Service.mk { r: Request =>
-      Ok(preAllocatedHelloWorldText)
-        .withHeaders("Server" -> "Example", "Date" -> RFC_1123_DATE_TIME.format(now()))
-    }
+  val addServerAndDate = Filter.mk[Request, Response, Request, Response] { (req, svc) =>
+    svc(req).map(resp => {
+      resp.headerMap("Server") = "Example"
+      resp.headerMap("Date") = dateFormat.format(System.currentTimeMillis())
+      resp
+    })
   }
   }
 
 
-  val jsonHelloWorld = {
-    import io.fintrospect.formats.Json4sJackson.ResponseBuilder.implicits._
-    Service.mk { r: Request => Ok(obj("message" -> string("Hello, World!")))
-      .withHeaders("Server" -> "Example", "Date" -> RFC_1123_DATE_TIME.format(now()))
-    }
-  }
+  val dbHost = Properties.envOrNone("DBHOST").map(Host(_)).getOrElse(Host.localhost)
+  val database = Database(dbHost)
 
 
-  val module = ModuleSpec(Root)
-    .withRoute(RouteSpec().at(Get) / "plaintext" bindTo plainTextHelloWorld)
-    .withRoute(RouteSpec().at(Get) / "json" bindTo jsonHelloWorld)
+  val module = ModuleSpec(Root, SimpleJson())
+    .withRoute(JsonRoute())
+    .withRoute(PlainTextRoute())
+    .withRoute(FortunesRoute(database))
+    .withRoutes(DatabaseRoutes(database))
 
 
   Await.ready(
   Await.ready(
     Http.server
     Http.server
@@ -42,6 +40,6 @@ object FintrospectBenchmarkServer extends App {
       .withStatsReceiver(NullStatsReceiver)
       .withStatsReceiver(NullStatsReceiver)
       .withTracer(NullTracer)
       .withTracer(NullTracer)
       .withMonitor(NullMonitor)
       .withMonitor(NullMonitor)
-      .serve(":9000", module.toService)
+      .serve(":9000", addServerAndDate.andThen(module.toService))
   )
   )
 }
 }

+ 42 - 0
frameworks/Scala/fintrospect/src/main/scala/FortunesRoute.scala

@@ -0,0 +1,42 @@
+import com.twitter.finagle.Service
+import com.twitter.finagle.http.Method.Get
+import com.twitter.finagle.http.Request
+import com.twitter.finagle.mysql.{Client, IntValue, Result, ResultSet, StringValue}
+import io.fintrospect.RouteSpec
+import io.fintrospect.RouteSpec.RequestValidation.none
+import io.fintrospect.formats.Html
+import io.fintrospect.templating.MustacheTemplates.CachingClasspath
+import io.fintrospect.templating.{RenderView, View}
+
+case class Fortune(id: Int, message: String)
+
+case class FortunesList(items: Seq[Fortune]) extends View
+
+object FortunesRoute {
+
+  private val toFortunes: PartialFunction[Result, Seq[Fortune]] = {
+    case rs: ResultSet => rs.rows
+      .map(row => {
+        val IntValue(id) = row("id").get
+        val StringValue(message) = row("message").get
+        Fortune(id, message)
+      })
+    case _ => Seq.empty
+  }
+
+  def apply(database: Client) = {
+
+    val statement = database.prepare("SELECT * FROM fortune")
+
+    val service = new RenderView(Html.ResponseBuilder, CachingClasspath()).andThen(
+      Service.mk {
+        r: Request =>
+          statement().map(toFortunes).map(f => {
+            val sortedFortunes = (Fortune(0, "Additional fortune added at request time.") +: f).sortBy(_.message)
+            FortunesList(sortedFortunes)
+          })
+      })
+
+    RouteSpec(validation = none).at(Get) / "fortunes" bindTo service
+  }
+}

+ 15 - 0
frameworks/Scala/fintrospect/src/main/scala/JsonRoute.scala

@@ -0,0 +1,15 @@
+import com.twitter.finagle.Service
+import com.twitter.finagle.http.Method.Get
+import com.twitter.finagle.http.Request
+import com.twitter.finagle.http.Status.Ok
+import io.fintrospect.RouteSpec
+import io.fintrospect.RouteSpec.RequestValidation.none
+import io.fintrospect.formats.Json4sJackson.JsonFormat.{obj, string}
+import io.fintrospect.formats.Json4sJackson.ResponseBuilder.implicits._
+
+object JsonRoute {
+
+  private val service = Service.mk { r: Request => Ok(obj("message" -> string("Hello, World!"))) }
+
+  def apply() = RouteSpec(validation = none).at(Get) / "json" bindTo service
+}

+ 17 - 0
frameworks/Scala/fintrospect/src/main/scala/PlainTextRoute.scala

@@ -0,0 +1,17 @@
+import com.twitter.finagle.Service
+import com.twitter.finagle.http.Method.Get
+import com.twitter.finagle.http.Request
+import com.twitter.finagle.http.Status.Ok
+import com.twitter.io.Buf
+import io.fintrospect.RouteSpec
+import io.fintrospect.RouteSpec.RequestValidation.none
+import io.fintrospect.formats.PlainText.ResponseBuilder.implicits._
+
+object PlainTextRoute {
+
+  private val preallocatedMsgForPlainText = Buf.Utf8("Hello, World!")
+
+  private val service = Service.mk { r: Request => Ok(preallocatedMsgForPlainText) }
+
+  def apply() = RouteSpec(validation = none).at(Get) / "plaintext" bindTo service
+}

+ 1 - 1
toolset/setup/linux/webservers/resin.sh

@@ -7,7 +7,7 @@ RETCODE=$(fw_exists ${IROOT}/resin.installed)
   source $IROOT/resin.installed
   source $IROOT/resin.installed
   return 0; }
   return 0; }
 
 
-RVER=4.0.41
+RVER=4.0.48
 RESIN=resin-$RVER
 RESIN=resin-$RVER
 RESIN_HOME=$IROOT/$RESIN
 RESIN_HOME=$IROOT/$RESIN