Browse Source

Keep undertow and undertow-postgresql, remove the other variants (#3626)

In general I think it's better for our test implementations to show one
good way of doing things with a given framework than it is to lay out
several options.  Based on previous results and the way the code turns
out, using postgresql with the official JDBC client is (in my opinion)
better than the other options for database access with this framework.
Michael Hixson 7 years ago
parent
commit
c5cdb815d4
31 changed files with 250 additions and 1611 deletions
  1. 0 111
      frameworks/Java/undertow/README.md
  2. 0 84
      frameworks/Java/undertow/benchmark_config.json
  3. 0 45
      frameworks/Java/undertow/maven-version-rules.xml
  4. 0 48
      frameworks/Java/undertow/pom.xml
  5. 0 34
      frameworks/Java/undertow/src/main/java/hello/AsyncHandler.java
  6. 0 39
      frameworks/Java/undertow/src/main/java/hello/DbMongoAsyncHandler.java
  7. 0 32
      frameworks/Java/undertow/src/main/java/hello/DbMongoHandler.java
  8. 0 44
      frameworks/Java/undertow/src/main/java/hello/DbPgAsyncHandler.java
  9. 0 40
      frameworks/Java/undertow/src/main/java/hello/DbSqlHandler.java
  10. 0 21
      frameworks/Java/undertow/src/main/java/hello/Fortune.java
  11. 0 40
      frameworks/Java/undertow/src/main/java/hello/FortunesMongoAsyncHandler.java
  12. 0 34
      frameworks/Java/undertow/src/main/java/hello/FortunesMongoHandler.java
  13. 0 46
      frameworks/Java/undertow/src/main/java/hello/FortunesPgAsyncHandler.java
  14. 0 42
      frameworks/Java/undertow/src/main/java/hello/FortunesSqlHandler.java
  15. 250 174
      frameworks/Java/undertow/src/main/java/hello/HelloWebServer.java
  16. 0 139
      frameworks/Java/undertow/src/main/java/hello/Helper.java
  17. 0 18
      frameworks/Java/undertow/src/main/java/hello/JsonHandler.java
  18. 0 32
      frameworks/Java/undertow/src/main/java/hello/PlaintextHandler.java
  19. 0 70
      frameworks/Java/undertow/src/main/java/hello/QueriesMongoAsyncHandler.java
  20. 0 37
      frameworks/Java/undertow/src/main/java/hello/QueriesMongoHandler.java
  21. 0 76
      frameworks/Java/undertow/src/main/java/hello/QueriesPgAsyncHandler.java
  22. 0 44
      frameworks/Java/undertow/src/main/java/hello/QueriesSqlHandler.java
  23. 0 100
      frameworks/Java/undertow/src/main/java/hello/UpdatesMongoAsyncHandler.java
  24. 0 51
      frameworks/Java/undertow/src/main/java/hello/UpdatesMongoHandler.java
  25. 0 100
      frameworks/Java/undertow/src/main/java/hello/UpdatesPgAsyncHandler.java
  26. 0 56
      frameworks/Java/undertow/src/main/java/hello/UpdatesSqlHandler.java
  27. 0 14
      frameworks/Java/undertow/src/main/java/hello/World.java
  28. 0 10
      frameworks/Java/undertow/undertow-mongodb-async.dockerfile
  29. 0 10
      frameworks/Java/undertow/undertow-mongodb.dockerfile
  30. 0 10
      frameworks/Java/undertow/undertow-mysql.dockerfile
  31. 0 10
      frameworks/Java/undertow/undertow-postgresql-async.dockerfile

+ 0 - 111
frameworks/Java/undertow/README.md

@@ -1,111 +0,0 @@
-# Undertow
-
-This is the test for the Undertow web server.
-
-* [Project website](http://undertow.io/)
-* [GitHub repository](https://github.com/undertow-io/undertow)
-
-## Test types
-
-This implements all benchmark test types.  The database tests are implemented
-for MySQL, PostgreSQL, and MongoDB databases.
-
-[HelloWebServer.java](src/main/java/hello/HelloWebServer.java) is the entry
-point for the application, providing the `main` method.
-
-**The only test type that exercises Undertow in isolation is the plaintext
-test**.  For functionality that Undertow does not provide — JSON encoding,
-database connectivity — this implementation depends on popular third party
-libraries that are expected to perform well.  We hope for these tests to serve
-as performance baselines for benchmarks of other frameworks that are implemented
-on top of Undertow.
-
-### Plaintext
-
-URL: `http://TFB-server:8080/plaintext`
-
-Source code:
-* [PlaintextHandler.java](src/main/java/hello/PlaintextHandler.java)
-
-Additional libraries used: (None)
-
-### JSON
-
-URL: `http://TFB-server:8080/json`
-
-Source code:
-* [JsonHandler.java](src/main/java/hello/JsonHandler.java)
-
-Additional libraries used:
-* [Jackson]
-
-### Database single-query
-
-URL: `http://TFB-server:8080/db`
-
-Source code:
-* [DbSqlHandler.java](src/main/java/hello/DbSqlHandler.java)
-* [DbMongoHandler.java](src/main/java/hello/DbMongoHandler.java)
-* [DbMongoAsyncHandler.java](src/main/java/hello/DbMongoAsyncHandler.java)
-
-Additional libraries used:
-* [Jackson]
-* [MySQL Connector/J]
-* [PostgreSQL JDBC Driver]
-* [Java MongoDB Driver]
-* [HikariCP]
-
-### Database multi-query
-
-URL: `http://TFB-server:8080/queries?queries={integer}`
-
-Source code:
-* [QueriesSqlHandler.java](src/main/java/hello/QueriesSqlHandler.java)
-* [QueriesMongoHandler.java](src/main/java/hello/QueriesMongoHandler.java)
-* [QueriesMongoAsyncHandler.java](src/main/java/hello/QueriesMongoAsyncHandler.java)
-
-Additional libraries used:
-* [Jackson]
-* [MySQL Connector/J]
-* [PostgreSQL JDBC Driver]
-* [Java MongoDB Driver]
-* [HikariCP]
-
-### Database updates
-
-URL: `http://TFB-server:8080/updates?queries={integer}`
-
-Source code:
-* [UpdatesSqlHandler.java](src/main/java/hello/UpdatesSqlHandler.java)
-* [UpdatesMongoHandler.java](src/main/java/hello/UpdatesMongoHandler.java)
-* [UpdatesMongoAsyncHandler.java](src/main/java/hello/UpdatesMongoAsyncHandler.java)
-
-Additional libraries used:
-* [Jackson]
-* [MySQL Connector/J]
-* [PostgreSQL JDBC Driver]
-* [Java MongoDB Driver]
-* [HikariCP]
-
-### Fortunes
-
-URL: `http://TFB-server:8080/fortunes`
-
-Source code:
-* [FortunesSqlHandler.java](src/main/java/hello/FortunesSqlHandler.java)
-* [FortunesMongoHandler.java](src/main/java/hello/FortunesMongoHandler.java)
-* [FortunesMongoAsyncHandler.java](src/main/java/hello/FortunesMongoAsyncHandler.java)
-
-Additional libraries used:
-* [Mustache.java]
-* [MySQL Connector/J]
-* [PostgreSQL JDBC Driver]
-* [Java MongoDB Driver]
-* [HikariCP]
-
-[Jackson]: https://github.com/FasterXML/Jackson
-[Mustache.java]: https://github.com/spullara/mustache.java
-[MySQL Connector/J]: https://dev.mysql.com/downloads/connector/j/5.1.html
-[PostgreSQL JDBC Driver]: https://jdbc.postgresql.org/
-[Java MongoDB Driver]: https://docs.mongodb.com/ecosystem/drivers/java/
-[HikariCP]: https://github.com/brettwooldridge/HikariCP

+ 0 - 84
frameworks/Java/undertow/benchmark_config.json

@@ -20,27 +20,6 @@
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
     },
     },
-    "mysql" : {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "MySQL",
-      "framework": "None",
-      "language": "Java",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "Undertow",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "undertow-mysql",
-      "notes": "",
-      "versus": ""
-    },
     "postgresql" : {
     "postgresql" : {
       "db_url": "/db",
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "query_url": "/queries?queries=",
@@ -61,69 +40,6 @@
       "display_name": "undertow-postgresql",
       "display_name": "undertow-postgresql",
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
-    },
-    "postgresql-async" : {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "Postgres",
-      "framework": "None",
-      "language": "Java",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "Undertow",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "undertow-postgresql-reactive",
-      "notes": "",
-      "versus": ""
-    },
-    "mongodb" : {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "MongoDB",
-      "framework": "None",
-      "language": "Java",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "Undertow",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "undertow-mongodb",
-      "notes": "",
-      "versus": ""
-    },
-    "mongodb-async" : {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "MongoDB",
-      "framework": "None",
-      "language": "Java",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "Undertow",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "undertow-mongodb-async",
-      "notes": "",
-      "versus": ""
     }
     }
   }]
   }]
 }
 }

+ 0 - 45
frameworks/Java/undertow/maven-version-rules.xml

@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ruleset comparisonMethod="maven"
-         xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0
-                             http://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd">
-
-  <rules>
-
-    <rule groupId="io.undertow" artifactId="undertow-core">
-      <ignoreVersions>
-        <ignoreVersion type="regex">.+\.Alpha[\d]+</ignoreVersion>
-        <ignoreVersion type="regex">.+\.Beta[\d]+</ignoreVersion>
-      </ignoreVersions>
-    </rule>
-
-    <rule groupId="com.fasterxml.jackson.*">
-      <ignoreVersions>
-        <ignoreVersion type="regex">.+\.pr[\d]+</ignoreVersion>
-      </ignoreVersions>
-    </rule>
-
-    <rule groupId="org.postgresql" artifactId="postgresql">
-      <ignoreVersions>
-        <ignoreVersion type="regex">.+\.jre6</ignoreVersion>
-        <ignoreVersion type="regex">.+\.jre7</ignoreVersion>
-      </ignoreVersions>
-    </rule>
-
-    <rule groupId="mysql" artifactId="mysql-connector-java">
-      <ignoreVersions>
-        <ignoreVersion type="regex">6\..*</ignoreVersion>
-        <ignoreVersion type="regex">8\..*</ignoreVersion>
-      </ignoreVersions>
-    </rule>
-
-    <rule groupId="org.mongodb">
-      <ignoreVersions>
-        <ignoreVersion type="regex">.+\-rc[\d]+</ignoreVersion>
-      </ignoreVersions>
-    </rule>
-
-  </rules>
-
-</ruleset>

+ 0 - 48
frameworks/Java/undertow/pom.xml

@@ -16,15 +16,10 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <hikaricp.version>3.1.0</hikaricp.version>
     <hikaricp.version>3.1.0</hikaricp.version>
     <jackson.version>2.9.5</jackson.version>
     <jackson.version>2.9.5</jackson.version>
-    <jaxb.version>2.3.0</jaxb.version>
     <maven-shade-plugin.version>3.1.1</maven-shade-plugin.version>
     <maven-shade-plugin.version>3.1.1</maven-shade-plugin.version>
-    <mongodb.version>3.6.3</mongodb.version>
     <mustache.version>0.9.5</mustache.version>
     <mustache.version>0.9.5</mustache.version>
-    <mysql.version>5.1.46</mysql.version>
     <postgresql.version>42.2.2</postgresql.version>
     <postgresql.version>42.2.2</postgresql.version>
-    <reactive-pg-client.version>0.7.0</reactive-pg-client.version>
     <undertow.version>2.0.4.Final</undertow.version>
     <undertow.version>2.0.4.Final</undertow.version>
-    <versions-maven-plugin.version>2.5</versions-maven-plugin.version>
   </properties>
   </properties>
 
 
   <prerequisites>
   <prerequisites>
@@ -37,36 +32,11 @@
       <artifactId>undertow-core</artifactId>
       <artifactId>undertow-core</artifactId>
       <version>${undertow.version}</version>
       <version>${undertow.version}</version>
     </dependency>
     </dependency>
-    <dependency>
-      <groupId>mysql</groupId>
-      <artifactId>mysql-connector-java</artifactId>
-      <version>${mysql.version}</version>
-    </dependency>
     <dependency>
     <dependency>
       <groupId>org.postgresql</groupId>
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
       <artifactId>postgresql</artifactId>
       <version>${postgresql.version}</version>
       <version>${postgresql.version}</version>
     </dependency>
     </dependency>
-    <dependency>
-      <groupId>org.mongodb</groupId>
-      <artifactId>mongodb-driver</artifactId>
-      <version>${mongodb.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.mongodb</groupId>
-      <artifactId>mongodb-driver-core</artifactId>
-      <version>${mongodb.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.mongodb</groupId>
-      <artifactId>mongodb-driver-async</artifactId>
-      <version>${mongodb.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.mongodb</groupId>
-      <artifactId>bson</artifactId>
-      <version>${mongodb.version}</version>
-    </dependency>
     <dependency>
     <dependency>
       <groupId>com.zaxxer</groupId>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
       <artifactId>HikariCP</artifactId>
@@ -82,29 +52,11 @@
       <artifactId>compiler</artifactId>
       <artifactId>compiler</artifactId>
       <version>${mustache.version}</version>
       <version>${mustache.version}</version>
     </dependency>
     </dependency>
-    <dependency>
-      <groupId>io.reactiverse</groupId>
-      <artifactId>reactive-pg-client</artifactId>
-      <version>${reactive-pg-client.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>javax.xml.bind</groupId>
-      <artifactId>jaxb-api</artifactId>
-      <version>${jaxb.version}</version>
-    </dependency>
   </dependencies>
   </dependencies>
 
 
   <build>
   <build>
     <finalName>app</finalName>
     <finalName>app</finalName>
     <plugins>
     <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>versions-maven-plugin</artifactId>
-        <version>${versions-maven-plugin.version}</version>
-        <configuration>
-          <rulesUri>file://${basedir}/maven-version-rules.xml</rulesUri>
-        </configuration>
-      </plugin>
       <plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
         <artifactId>maven-shade-plugin</artifactId>

+ 0 - 34
frameworks/Java/undertow/src/main/java/hello/AsyncHandler.java

@@ -1,34 +0,0 @@
-package hello;
-
-import static hello.Helper.sendException;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import io.undertow.util.SameThreadExecutor;
-import java.util.Objects;
-
-/**
- * An HTTP handler that <em>does not</em> end the exchange when the call stack
- * of its {@link HttpHandler#handleRequest(HttpServerExchange)} method returns.
- * The handler must ensure that every exchange is ended through other means.
- */
-final class AsyncHandler implements HttpHandler {
-  private final HttpHandler handler;
-
-  AsyncHandler(HttpHandler handler) {
-    this.handler = Objects.requireNonNull(handler);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    Runnable asyncTask =
-        () -> {
-          try {
-            handler.handleRequest(exchange);
-          } catch (Exception e) {
-            sendException(exchange, e);
-          }
-        };
-    exchange.dispatch(SameThreadExecutor.INSTANCE, asyncTask);
-  }
-}

+ 0 - 39
frameworks/Java/undertow/src/main/java/hello/DbMongoAsyncHandler.java

@@ -1,39 +0,0 @@
-package hello;
-
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendException;
-import static hello.Helper.sendJson;
-
-import com.mongodb.async.client.MongoCollection;
-import com.mongodb.async.client.MongoDatabase;
-import com.mongodb.client.model.Filters;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import org.bson.Document;
-
-/**
- * Handles the single-query database test using MongoDB with an asynchronous
- * API.
- */
-final class DbMongoAsyncHandler implements HttpHandler {
-  private final MongoCollection<Document> worldCollection;
-
-  DbMongoAsyncHandler(MongoDatabase db) {
-    worldCollection = db.getCollection("world");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    worldCollection
-        .find(Filters.eq(randomWorldNumber()))
-        .map(Helper::mongoDocumentToWorld)
-        .first(
-            (world, exception) -> {
-              if (exception != null) {
-                sendException(exchange, exception);
-              } else {
-                sendJson(exchange, world);
-              }
-            });
-  }
-}

+ 0 - 32
frameworks/Java/undertow/src/main/java/hello/DbMongoHandler.java

@@ -1,32 +0,0 @@
-package hello;
-
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendJson;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.client.model.Filters;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import org.bson.Document;
-
-/**
- * Handles the single-query database test using MongoDB.
- */
-final class DbMongoHandler implements HttpHandler {
-  private final MongoCollection<Document> worldCollection;
-
-  DbMongoHandler(MongoDatabase db) {
-    worldCollection = db.getCollection("world");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    World world =
-        worldCollection
-            .find(Filters.eq(randomWorldNumber()))
-            .map(Helper::mongoDocumentToWorld)
-            .first();
-    sendJson(exchange, world);
-  }
-}

+ 0 - 44
frameworks/Java/undertow/src/main/java/hello/DbPgAsyncHandler.java

@@ -1,44 +0,0 @@
-package hello;
-
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendException;
-import static hello.Helper.sendJson;
-
-import io.reactiverse.pgclient.PgClient;
-import io.reactiverse.pgclient.PgResult;
-import io.reactiverse.pgclient.Row;
-import io.reactiverse.pgclient.Tuple;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import io.vertx.core.AsyncResult;
-import java.util.Objects;
-
-/**
- * Handles the single-query database test using PostgreSQL with an asynchronous
- * API.
- */
-final class DbPgAsyncHandler implements HttpHandler {
-  private final PgClient client;
-
-  DbPgAsyncHandler(PgClient client) {
-    this.client = Objects.requireNonNull(client);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    client.preparedQuery(
-        "SELECT id, randomnumber FROM World WHERE id = $1",
-        Tuple.of(randomWorldNumber()),
-        (AsyncResult<PgResult<Row>> result) -> {
-          if (result.failed()) {
-            sendException(exchange, result.cause());
-          } else {
-            Row row = result.result().iterator().next();
-            int id = row.getInteger(0);
-            int randomNumber = row.getInteger(1);
-            World world = new World(id, randomNumber);
-            sendJson(exchange, world);
-          }
-        });
-  }
-}

+ 0 - 40
frameworks/Java/undertow/src/main/java/hello/DbSqlHandler.java

@@ -1,40 +0,0 @@
-package hello;
-
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendJson;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Objects;
-import javax.sql.DataSource;
-
-/**
- * Handles the single-query database test using a SQL database.
- */
-final class DbSqlHandler implements HttpHandler {
-  private final DataSource db;
-
-  DbSqlHandler(DataSource db) {
-    this.db = Objects.requireNonNull(db);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) throws Exception {
-    World world;
-    try (Connection connection = db.getConnection();
-         PreparedStatement statement =
-             connection.prepareStatement("SELECT * FROM World WHERE id = ?")) {
-      statement.setInt(1, randomWorldNumber());
-      try (ResultSet resultSet = statement.executeQuery()) {
-        resultSet.next();
-        int id = resultSet.getInt("id");
-        int randomNumber = resultSet.getInt("randomNumber");
-        world = new World(id, randomNumber);
-      }
-    }
-    sendJson(exchange, world);
-  }
-}

+ 0 - 21
frameworks/Java/undertow/src/main/java/hello/Fortune.java

@@ -1,21 +0,0 @@
-package hello;
-
-import java.util.Objects;
-
-/**
- * The model for the "fortune" database table.
- */
-public final class Fortune implements Comparable<Fortune> {
-  public final int id;
-  public final String message;
-
-  public Fortune(int id, String message) {
-    this.id = id;
-    this.message = Objects.requireNonNull(message);
-  }
-
-  @Override
-  public int compareTo(Fortune other) {
-    return message.compareTo(other.message);
-  }
-}

+ 0 - 40
frameworks/Java/undertow/src/main/java/hello/FortunesMongoAsyncHandler.java

@@ -1,40 +0,0 @@
-package hello;
-
-import static hello.Helper.sendException;
-import static hello.Helper.sendHtml;
-
-import com.mongodb.async.client.MongoCollection;
-import com.mongodb.async.client.MongoDatabase;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.util.ArrayList;
-import org.bson.Document;
-
-/**
- * Handles the fortunes test using MongoDB with an asynchronous API.
- */
-final class FortunesMongoAsyncHandler implements HttpHandler {
-  private final MongoCollection<Document> fortuneCollection;
-
-  FortunesMongoAsyncHandler(MongoDatabase db) {
-    fortuneCollection = db.getCollection("fortune");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    fortuneCollection
-        .find()
-        .map(Helper::mongoDocumentToFortune)
-        .into(
-            new ArrayList<>(),
-            (fortunes, exception) -> {
-              if (exception != null) {
-                sendException(exchange, exception);
-              } else {
-                fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-                fortunes.sort(null);
-                sendHtml(exchange, fortunes, "hello/fortunes.mustache");
-              }
-            });
-  }
-}

+ 0 - 34
frameworks/Java/undertow/src/main/java/hello/FortunesMongoHandler.java

@@ -1,34 +0,0 @@
-package hello;
-
-import static hello.Helper.sendHtml;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.util.ArrayList;
-import java.util.List;
-import org.bson.Document;
-
-/**
- * Handles the fortunes test using MongoDB.
- */
-final class FortunesMongoHandler implements HttpHandler {
-  private final MongoCollection<Document> fortuneCollection;
-
-  FortunesMongoHandler(MongoDatabase db) {
-    fortuneCollection = db.getCollection("fortune");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    List<Fortune> fortunes =
-        fortuneCollection
-            .find()
-            .map(Helper::mongoDocumentToFortune)
-            .into(new ArrayList<>());
-    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-    fortunes.sort(null);
-    sendHtml(exchange, fortunes, "hello/fortunes.mustache");
-  }
-}

+ 0 - 46
frameworks/Java/undertow/src/main/java/hello/FortunesPgAsyncHandler.java

@@ -1,46 +0,0 @@
-package hello;
-
-import static hello.Helper.sendException;
-import static hello.Helper.sendHtml;
-
-import io.reactiverse.pgclient.PgClient;
-import io.reactiverse.pgclient.PgResult;
-import io.reactiverse.pgclient.Row;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import io.vertx.core.AsyncResult;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Handles the fortunes test using PostgreSQL with an asynchronous API.
- */
-final class FortunesPgAsyncHandler implements HttpHandler {
-  private final PgClient client;
-
-  FortunesPgAsyncHandler(PgClient client) {
-    this.client = Objects.requireNonNull(client);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    client.preparedQuery(
-        "SELECT id, message FROM Fortune",
-        (AsyncResult<PgResult<Row>> result) -> {
-          if (result.failed()) {
-            sendException(exchange, result.cause());
-          } else {
-            List<Fortune> fortunes = new ArrayList<>();
-            for (Row row : result.result()) {
-              int id = row.getInteger(0);
-              String message = row.getString(1);
-              fortunes.add(new Fortune(id, message));
-            }
-            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-            fortunes.sort(null);
-            sendHtml(exchange, fortunes, "hello/fortunes.mustache");
-          }
-        });
-  }
-}

+ 0 - 42
frameworks/Java/undertow/src/main/java/hello/FortunesSqlHandler.java

@@ -1,42 +0,0 @@
-package hello;
-
-import static hello.Helper.sendHtml;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import javax.sql.DataSource;
-
-/**
- * Handles the fortunes test using a SQL database.
- */
-final class FortunesSqlHandler implements HttpHandler {
-  private final DataSource db;
-
-  FortunesSqlHandler(DataSource db) {
-    this.db = Objects.requireNonNull(db);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) throws Exception {
-    List<Fortune> fortunes = new ArrayList<>();
-    try (Connection connection = db.getConnection();
-         PreparedStatement statement =
-             connection.prepareStatement("SELECT * FROM Fortune");
-         ResultSet resultSet = statement.executeQuery()) {
-      while (resultSet.next()) {
-        int id = resultSet.getInt("id");
-        String message = resultSet.getString("message");
-        fortunes.add(new Fortune(id, message));
-      }
-    }
-    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-    fortunes.sort(null);
-    sendHtml(exchange, fortunes, "hello/fortunes.mustache");
-  }
-}

+ 250 - 174
frameworks/Java/undertow/src/main/java/hello/HelloWebServer.java

@@ -1,35 +1,40 @@
 package hello;
 package hello;
 
 
-import com.mongodb.MongoClient;
-import com.mongodb.MongoClientOptions;
-import com.mongodb.ServerAddress;
-import com.mongodb.async.client.MongoClientSettings;
-import com.mongodb.async.client.MongoClients;
-import com.mongodb.connection.ClusterConnectionMode;
-import com.mongodb.connection.ClusterSettings;
-import com.mongodb.connection.ConnectionPoolSettings;
+import static io.undertow.util.Headers.CONTENT_TYPE;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.Comparator.comparing;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.mustachejava.DefaultMustacheFactory;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 import com.zaxxer.hikari.HikariDataSource;
-import io.reactiverse.pgclient.PgClient;
-import io.reactiverse.pgclient.PgPoolOptions;
 import io.undertow.Undertow;
 import io.undertow.Undertow;
 import io.undertow.UndertowOptions;
 import io.undertow.UndertowOptions;
 import io.undertow.server.HttpHandler;
 import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
 import io.undertow.server.handlers.BlockingHandler;
 import io.undertow.server.handlers.BlockingHandler;
 import io.undertow.server.handlers.PathHandler;
 import io.undertow.server.handlers.PathHandler;
 import io.undertow.server.handlers.SetHeaderHandler;
 import io.undertow.server.handlers.SetHeaderHandler;
-import java.util.List;
-import java.util.function.Supplier;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+import javax.sql.DataSource;
 
 
-/**
- * Provides the {@link #main(String[])} method, which launches the application.
- */
 public final class HelloWebServer {
 public final class HelloWebServer {
   private HelloWebServer() {
   private HelloWebServer() {
     throw new AssertionError();
     throw new AssertionError();
   }
   }
 
 
+  enum Mode { NO_DATABASE, POSTGRESQL }
+
   public static void main(String[] args) {
   public static void main(String[] args) {
+    var mode = Mode.valueOf(args[0]);
+    var handler = serverHeaderHandler(pathHandler(mode));
+
     Undertow
     Undertow
         .builder()
         .builder()
         .addHttpListener(8080, "0.0.0.0")
         .addHttpListener(8080, "0.0.0.0")
@@ -37,171 +42,242 @@ public final class HelloWebServer {
         // Adding a "Connection: keep-alive" header to every response would only
         // Adding a "Connection: keep-alive" header to every response would only
         // add useless bytes.
         // add useless bytes.
         .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
         .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
-        .setHandler(ServerMode.valueOf(args[0]).createRootHandler())
+        .setHandler(handler)
         .build()
         .build()
         .start();
         .start();
   }
   }
 
 
-  static final int MAX_DB_REQUEST_CONCURRENCY = 512;
-  static final int MAX_DB_QUERIES_PER_REQUEST = 20;
-
-  enum ServerMode {
-    /**
-     * The server will only implement the test types that do not require a
-     * database.
-     */
-    NO_DATABASE(() -> {
-      var handler = new PathHandler();
-      handler.addExactPath("/plaintext", new PlaintextHandler());
-      handler.addExactPath("/json", new JsonHandler());
-      return handler;
-    }),
-
-    /**
-     * The server will use a MySQL database and will only implement the test
-     * types that require a database.
-     */
-    MYSQL(() -> {
-      var config = new HikariConfig();
-      config.setJdbcUrl("jdbc:mysql://tfb-database:3306/hello_world?useSSL=false&useServerPrepStmts=true&cachePrepStmts=true");
-      config.setUsername("benchmarkdbuser");
-      config.setPassword("benchmarkdbpass");
-      config.setMaximumPoolSize(48);
-
-      var db = new HikariDataSource(config);
-
-      var handler = new PathHandler();
-      handler.addExactPath("/db", new BlockingHandler(new DbSqlHandler(db)));
-      handler.addExactPath("/queries", new BlockingHandler(new QueriesSqlHandler(db)));
-      handler.addExactPath("/fortunes", new BlockingHandler(new FortunesSqlHandler(db)));
-      handler.addExactPath("/updates", new BlockingHandler(new UpdatesSqlHandler(db)));
-      return handler;
-    }),
-
-    /**
-     * The server will use a PostgreSQL database and will only implement the
-     * test types that require a database.
-     */
-    POSTGRESQL(() -> {
-      var config = new HikariConfig();
-      config.setJdbcUrl("jdbc:postgresql://tfb-database:5432/hello_world");
-      config.setUsername("benchmarkdbuser");
-      config.setPassword("benchmarkdbpass");
-      config.setMaximumPoolSize(48);
-
-      var db = new HikariDataSource(config);
-
-      var handler = new PathHandler();
-      handler.addExactPath("/db", new BlockingHandler(new DbSqlHandler(db)));
-      handler.addExactPath("/queries", new BlockingHandler(new QueriesSqlHandler(db)));
-      handler.addExactPath("/fortunes", new BlockingHandler(new FortunesSqlHandler(db)));
-      handler.addExactPath("/updates", new BlockingHandler(new UpdatesSqlHandler(db)));
-      return handler;
-    }),
-
-    /**
-     * The server will use a PostgreSQL database with an asynchronous API and
-     * will only implement the test types that require a database.
-     */
-    POSTGRESQL_ASYNC(() -> {
-      var options = new PgPoolOptions();
-      options.setHost("tfb-database");
-      options.setPort(5432);
-      options.setDatabase("hello_world");
-      options.setUsername("benchmarkdbuser");
-      options.setPassword("benchmarkdbpass");
-      options.setCachePreparedStatements(true);
-      options.setMaxSize(1); // Without this, the updates test breaks.
-
-      var client = PgClient.pool(options);
-
-      var handler = new PathHandler();
-      handler.addExactPath("/db", new AsyncHandler(new DbPgAsyncHandler(client)));
-      handler.addExactPath("/queries", new AsyncHandler(new QueriesPgAsyncHandler(client)));
-      handler.addExactPath("/fortunes", new AsyncHandler(new FortunesPgAsyncHandler(client)));
-      handler.addExactPath("/updates", new AsyncHandler(new UpdatesPgAsyncHandler(client)));
-      return handler;
-    }),
-
-    /**
-     * The server will use a MongoDB database and will only implement the test
-     * types that require a database.
-     */
-    MONGODB(() -> {
-      int connectionPoolSize = 256;
-
-      var options = MongoClientOptions.builder();
-      options.connectionsPerHost(connectionPoolSize);
-      options.threadsAllowedToBlockForConnectionMultiplier(
-          (int) Math.ceil((double) MAX_DB_REQUEST_CONCURRENCY / connectionPoolSize));
-
-      var client = new MongoClient("tfb-database:27017", options.build());
-
-      var db = client.getDatabase("hello_world");
-
-      var handler = new PathHandler();
-      handler.addExactPath("/db", new BlockingHandler(new DbMongoHandler(db)));
-      handler.addExactPath("/queries", new BlockingHandler(new QueriesMongoHandler(db)));
-      handler.addExactPath("/fortunes", new BlockingHandler(new FortunesMongoHandler(db)));
-      handler.addExactPath("/updates", new BlockingHandler(new UpdatesMongoHandler(db)));
-      return handler;
-    }),
-
-    /**
-     * The server will use a MongoDB database with an asynchronous API and will
-     * only implement the test types that require a database.
-     */
-    MONGODB_ASYNC(() -> {
-      int connectionPoolSize = 256;
-
-      var clusterSettings =
-          ClusterSettings
-              .builder()
-              .mode(ClusterConnectionMode.SINGLE)
-              .hosts(List.of(new ServerAddress("tfb-database:27017")))
-              .build();
-
-      var connectionPoolSettings =
-          ConnectionPoolSettings
-              .builder()
-              .maxSize(connectionPoolSize)
-              .maxWaitQueueSize(MAX_DB_REQUEST_CONCURRENCY * MAX_DB_QUERIES_PER_REQUEST)
-              .build();
-
-      var clientSettings =
-          MongoClientSettings
-              .builder()
-              .clusterSettings(clusterSettings)
-              .connectionPoolSettings(connectionPoolSettings)
-              .build();
-
-      var client = MongoClients.create(clientSettings);
-
-      var db = client.getDatabase("hello_world");
-
-      var handler = new PathHandler();
-      handler.addExactPath("/db", new AsyncHandler(new DbMongoAsyncHandler(db)));
-      handler.addExactPath("/queries", new AsyncHandler(new QueriesMongoAsyncHandler(db)));
-      handler.addExactPath("/fortunes", new AsyncHandler(new FortunesMongoAsyncHandler(db)));
-      handler.addExactPath("/updates", new AsyncHandler(new UpdatesMongoAsyncHandler(db)));
-      return handler;
-    });
-
-    private final Supplier<HttpHandler> routerSupplier;
-
-    ServerMode(Supplier<HttpHandler> routerSupplier) {
-      this.routerSupplier = routerSupplier;
+  static HttpHandler serverHeaderHandler(HttpHandler next) {
+    return new SetHeaderHandler(next, "Server", "U-tow");
+  }
+
+  static HttpHandler pathHandler(Mode mode) {
+    switch (mode) {
+      case NO_DATABASE: return noDatabasePathHandler();
+      case POSTGRESQL:  return postgresqlPathHandler();
     }
     }
+    throw new AssertionError(mode);
+  }
+
+  static HttpHandler noDatabasePathHandler() {
+    return new PathHandler()
+        .addExactPath("/plaintext", plaintextHandler())
+        .addExactPath("/json", jsonHandler());
+  }
+
+  static HttpHandler postgresqlPathHandler() {
+    var config = new HikariConfig();
+    config.setJdbcUrl("jdbc:postgresql://tfb-database:5432/hello_world");
+    config.setUsername("benchmarkdbuser");
+    config.setPassword("benchmarkdbpass");
+    config.setMaximumPoolSize(48);
+
+    var db = new HikariDataSource(config);
+
+    return new BlockingHandler(
+        new PathHandler()
+            .addExactPath("/db", dbHandler(db))
+            .addExactPath("/queries", queriesHandler(db))
+            .addExactPath("/fortunes", fortunesHandler(db))
+            .addExactPath("/updates", updatesHandler(db)));
+  }
+
+  static HttpHandler plaintextHandler() {
+    // Normally, one would send the string "Hello, World!" directly.  Reusing a
+    // ByteBuffer is a micro-optimization that is explicitly permitted by the
+    // plaintext test requirements.
+
+    var bytes = "Hello, World!".getBytes(US_ASCII);
+    var buffer = ByteBuffer.allocateDirect(bytes.length);
+    buffer.put(bytes);
+    buffer.flip();
+
+    return exchange -> {
+      exchange.getResponseHeaders().put(CONTENT_TYPE, "text/plain");
+      exchange.getResponseSender().send(buffer.duplicate());
+    };
+  }
+
+  static HttpHandler jsonHandler() {
+    return exchange -> {
+      var value = Map.of("message", "Hello, World!");
+      sendJson(exchange, value);
+    };
+  }
+
+  static HttpHandler dbHandler(DataSource db) {
+    Objects.requireNonNull(db);
+
+    return exchange -> {
+      World world;
+
+      try (var connection = db.getConnection();
+           var statement =
+               connection.prepareStatement(
+                   "SELECT * FROM world WHERE id = ?")) {
+
+        statement.setInt(1, randomWorldNumber());
+        try (var resultSet = statement.executeQuery()) {
+          resultSet.next();
+          var id = resultSet.getInt("id");
+          var randomNumber = resultSet.getInt("randomnumber");
+          world = new World(id, randomNumber);
+        }
+      }
+
+      sendJson(exchange, world);
+    };
+  }
+
+  static HttpHandler queriesHandler(DataSource db) {
+    Objects.requireNonNull(db);
+
+    return exchange -> {
+      var worlds = new World[getQueries(exchange)];
+
+      try (var connection = db.getConnection();
+           var statement =
+               connection.prepareStatement(
+                   "SELECT * FROM world WHERE id = ?")) {
+
+        for (var i = 0; i < worlds.length; i++) {
+          statement.setInt(1, randomWorldNumber());
+          try (var resultSet = statement.executeQuery()) {
+            resultSet.next();
+            var id = resultSet.getInt("id");
+            var randomNumber = resultSet.getInt("randomnumber");
+            worlds[i] = new World(id, randomNumber);
+          }
+        }
+      }
+
+      sendJson(exchange, worlds);
+    };
+  }
+
+  static HttpHandler updatesHandler(DataSource db) {
+    Objects.requireNonNull(db);
+
+    return exchange -> {
+      var worlds = new World[getQueries(exchange)];
+
+      try (var connection = db.getConnection()) {
+        try (var statement =
+                 connection.prepareStatement(
+                     "SELECT * FROM world WHERE id = ?")) {
+
+          for (int i = 0; i < worlds.length; i++) {
+            statement.setInt(1, randomWorldNumber());
+            try (var resultSet = statement.executeQuery()) {
+              resultSet.next();
+              var id = resultSet.getInt("id");
+              var randomNumber = resultSet.getInt("randomnumber");
+              worlds[i] = new World(id, randomNumber);
+            }
+          }
+        }
+
+        try (var statement =
+                 connection.prepareStatement(
+                     "UPDATE world SET randomnumber = ? WHERE id = ?")) {
+
+          for (var world : worlds) {
+            world.randomNumber = randomWorldNumber();
+            statement.setInt(1, world.randomNumber);
+            statement.setInt(2, world.id);
+            statement.executeUpdate();
+          }
+        }
+      }
+
+      sendJson(exchange, worlds);
+    };
+  }
+
+  static HttpHandler fortunesHandler(DataSource db) {
+    Objects.requireNonNull(db);
+
+    var mustacheFactory = new DefaultMustacheFactory();
+
+    return exchange -> {
+      var fortunes = new ArrayList<Fortune>();
+
+      try (var connection = db.getConnection();
+           var statement = connection.prepareStatement("SELECT * FROM fortune");
+           var resultSet = statement.executeQuery()) {
+
+        while (resultSet.next()) {
+          var id = resultSet.getInt("id");
+          var message = resultSet.getString("message");
+          fortunes.add(new Fortune(id, message));
+        }
+      }
+
+      fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+      fortunes.sort(comparing(fortune -> fortune.message));
+
+      var mustache = mustacheFactory.compile("hello/fortunes.mustache");
+      var writer = new StringWriter();
+      mustache.execute(writer, fortunes);
+      var html = writer.toString();
+
+      exchange.getResponseHeaders().put(CONTENT_TYPE, "text/html;charset=utf-8");
+      exchange.getResponseSender().send(html);
+    };
+  }
+
+  static int getQueries(HttpServerExchange exchange) {
+    var values = exchange.getQueryParameters().get("queries");
+    if (values == null)
+      return 1;
+
+    var textValue = values.peekFirst();
+    if (textValue == null)
+      return 1;
+
+    int parsedValue;
+    try {
+      parsedValue = Integer.parseInt(textValue);
+    } catch (NumberFormatException e) {
+      return 1;
+    }
+
+    return Math.min(500, Math.max(1, parsedValue));
+  }
+
+  static int randomWorldNumber() {
+    return 1 + ThreadLocalRandom.current().nextInt(10000);
+  }
+
+  static void sendJson(HttpServerExchange exchange, Object value)
+      throws IOException {
+
+    var bytes = objectMapper.writeValueAsBytes(value);
+    var buffer = ByteBuffer.wrap(bytes);
+
+    exchange.getResponseHeaders().put(CONTENT_TYPE, "application/json");
+    exchange.getResponseSender().send(buffer);
+  }
+
+  private static final ObjectMapper objectMapper = new ObjectMapper();
+
+  public static final class Fortune {
+    public final int id;
+    public final String message;
+
+    public Fortune(int id, String message) {
+      this.id = id;
+      this.message = Objects.requireNonNull(message);
+    }
+  }
+
+  public static final class World {
+    public int id;
+    public int randomNumber;
 
 
-    /**
-     * Returns an HTTP handler that provides routing for all the
-     * test-type-specific endpoints of the server.
-     */
-    HttpHandler createRootHandler() {
-      return new SetHeaderHandler(
-          /* next= */ routerSupplier.get(),
-          /* header= */ "Server",
-          /* value= */ "U-tow");
+    public World(int id, int randomNumber) {
+      this.id = id;
+      this.randomNumber = randomNumber;
     }
     }
   }
   }
 }
 }

+ 0 - 139
frameworks/Java/undertow/src/main/java/hello/Helper.java

@@ -1,139 +0,0 @@
-package hello;
-
-import static io.undertow.util.Headers.CONTENT_TYPE;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.mustachejava.DefaultMustacheFactory;
-import com.github.mustachejava.Mustache;
-import com.github.mustachejava.MustacheFactory;
-import io.undertow.server.HttpServerExchange;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.ByteBuffer;
-import java.util.Deque;
-import java.util.concurrent.ThreadLocalRandom;
-import org.bson.Document;
-
-/**
- * Provides utility methods for the application.
- */
-final class Helper {
-  private Helper() {
-    throw new AssertionError();
-  }
-
-  /**
-   * Returns the value of the "queries" request parameter, which is an integer
-   * bound between 1 and 500 with a default value of 1.
-   *
-   * @param exchange the current HTTP exchange
-   * @return the value of the "queries" request parameter
-   */
-  static int getQueries(HttpServerExchange exchange) {
-    Deque<String> values = exchange.getQueryParameters().get("queries");
-    if (values == null) {
-      return 1;
-    }
-    String textValue = values.peekFirst();
-    if (textValue == null) {
-      return 1;
-    }
-    int parsedValue;
-    try {
-      parsedValue = Integer.parseInt(textValue);
-    } catch (NumberFormatException e) {
-      return 1;
-    }
-    return Math.min(500, Math.max(1, parsedValue));
-  }
-
-  /**
-   * Returns a random integer that is a suitable value for both the {@code id}
-   * and {@code randomNumber} properties of a world object.
-   *
-   * @return a random world number
-   */
-  static int randomWorldNumber() {
-    return 1 + ThreadLocalRandom.current().nextInt(10000);
-  }
-
-  /**
-   * Ends the HTTP exchange by encoding the given value as JSON and writing
-   * that JSON to the response.
-   *
-   * @param exchange the current HTTP exchange
-   * @param value the value to be encoded as JSON
-   * @throws IllegalArgumentException if the value cannot be encoded as JSON
-   */
-  static void sendJson(HttpServerExchange exchange, Object value) {
-    byte[] jsonBytes;
-    try {
-      jsonBytes = objectMapper.writeValueAsBytes(value);
-    } catch (IOException e) {
-      throw new IllegalArgumentException(e);
-    }
-    ByteBuffer jsonBuffer = ByteBuffer.wrap(jsonBytes);
-    exchange.getResponseHeaders().put(CONTENT_TYPE, "application/json");
-    exchange.getResponseSender().send(jsonBuffer);
-  }
-
-  private static final ObjectMapper objectMapper = new ObjectMapper();
-
-  /**
-   * Ends the HTTP exchange by supplying the given value to a Mustache template
-   * and writing the HTML output of the template to the response.
-   *
-   * @param exchange the current HTTP exchange
-   * @param value the value to be supplied to the Mustache template
-   * @param templatePath the path to the Mustache template
-   */
-  static void sendHtml(HttpServerExchange exchange,
-                       Object value,
-                       String templatePath) {
-    Mustache mustache = mustacheFactory.compile(templatePath);
-    StringWriter writer = new StringWriter();
-    mustache.execute(writer, value);
-    String html = writer.toString();
-    exchange.getResponseHeaders().put(CONTENT_TYPE, "text/html;charset=utf-8");
-    exchange.getResponseSender().send(html);
-  }
-
-  private static final MustacheFactory mustacheFactory =
-      new DefaultMustacheFactory();
-
-  /**
-   * Ends the HTTP exchange with an exception.
-   *
-   * @param exchange the current HTTP exchange
-   * @param exception the exception that was thrown
-   */
-  static void sendException(HttpServerExchange exchange, Throwable exception) {
-    exchange.setStatusCode(500);
-    exchange.endExchange();
-    exception.printStackTrace();
-  }
-
-  /**
-   * Reads a {@link World} from its persisted {@link Document} representation.
-   */
-  static World mongoDocumentToWorld(Document document) {
-    int id = mongoGetInt(document, "_id");
-    int randomNumber = mongoGetInt(document, "randomNumber");
-    return new World(id, randomNumber);
-  }
-
-  /**
-   * Reads a {@link Fortune} from its persisted {@link Document} representation.
-   */
-  static Fortune mongoDocumentToFortune(Document document) {
-    int id = mongoGetInt(document, "_id");
-    String message = document.getString("message");
-    return new Fortune(id, message);
-  }
-
-  // We don't know ahead of time whether these values are instances of Integer
-  // or Double.  This code is compatible with both.
-  private static int mongoGetInt(Document document, String key) {
-    return ((Number) document.get(key)).intValue();
-  }
-}

+ 0 - 18
frameworks/Java/undertow/src/main/java/hello/JsonHandler.java

@@ -1,18 +0,0 @@
-package hello;
-
-import static hello.Helper.sendJson;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.util.Map;
-
-/**
- * Handles the JSON test.
- */
-final class JsonHandler implements HttpHandler {
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    Map<String, String> value = Map.of("message", "Hello, World!");
-    sendJson(exchange, value);
-  }
-}

+ 0 - 32
frameworks/Java/undertow/src/main/java/hello/PlaintextHandler.java

@@ -1,32 +0,0 @@
-package hello;
-
-import static io.undertow.util.Headers.CONTENT_TYPE;
-import static java.nio.charset.StandardCharsets.US_ASCII;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.nio.ByteBuffer;
-
-/**
- * Handles the plaintext test.
- */
-final class PlaintextHandler implements HttpHandler {
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    exchange.getResponseHeaders().put(CONTENT_TYPE, "text/plain");
-    exchange.getResponseSender().send(buffer.duplicate());
-  }
-
-  // Normally, one would send the string "Hello, World!" directly.  Reusing a
-  // ByteBuffer is a micro-optimization that is explicitly permitted by the
-  // plaintext test requirements.
-
-  private static final ByteBuffer buffer;
-  static {
-    String message = "Hello, World!";
-    byte[] messageBytes = message.getBytes(US_ASCII);
-    buffer = ByteBuffer.allocateDirect(messageBytes.length);
-    buffer.put(messageBytes);
-    buffer.flip();
-  }
-}

+ 0 - 70
frameworks/Java/undertow/src/main/java/hello/QueriesMongoAsyncHandler.java

@@ -1,70 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendException;
-import static hello.Helper.sendJson;
-
-import com.mongodb.async.client.MongoCollection;
-import com.mongodb.async.client.MongoDatabase;
-import com.mongodb.client.model.Filters;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.util.concurrent.CompletableFuture;
-import org.bson.Document;
-
-/**
- * Handles the multi-query database test using MongoDB with an asynchronous API.
- */
-final class QueriesMongoAsyncHandler implements HttpHandler {
-  private final MongoCollection<Document> worldCollection;
-
-  QueriesMongoAsyncHandler(MongoDatabase db) {
-    worldCollection = db.getCollection("world");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    int queries = getQueries(exchange);
-    nWorlds(queries).whenComplete(
-        (worlds, exception) -> {
-          if (exception != null) {
-            sendException(exchange, exception);
-          } else {
-            sendJson(exchange, worlds);
-          }
-        });
-  }
-
-  private CompletableFuture<World[]> nWorlds(int n) {
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    CompletableFuture<World>[] futures = new CompletableFuture[n];
-    for (int i = 0; i < futures.length; i++) {
-      futures[i] = oneWorld();
-    }
-    return CompletableFuture.allOf(futures).thenApply(
-        nil -> {
-          World[] worlds = new World[futures.length];
-          for (int i = 0; i < futures.length; i++) {
-            worlds[i] = futures[i].join();
-          }
-          return worlds;
-        });
-  }
-
-  private CompletableFuture<World> oneWorld() {
-    CompletableFuture<World> future = new CompletableFuture<>();
-    worldCollection
-        .find(Filters.eq(randomWorldNumber()))
-        .map(Helper::mongoDocumentToWorld)
-        .first(
-            (world, exception) -> {
-              if (exception != null) {
-                future.completeExceptionally(exception);
-              } else {
-                future.complete(world);
-              }
-            });
-    return future;
-  }
-}

+ 0 - 37
frameworks/Java/undertow/src/main/java/hello/QueriesMongoHandler.java

@@ -1,37 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendJson;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.client.model.Filters;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import org.bson.Document;
-
-/**
- * Handles the multi-query database test using MongoDB.
- */
-final class QueriesMongoHandler implements HttpHandler {
-  private final MongoCollection<Document> worldCollection;
-
-  QueriesMongoHandler(MongoDatabase db) {
-    worldCollection = db.getCollection("world");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    int queries = getQueries(exchange);
-    World[] worlds = new World[queries];
-    for (int i = 0; i < worlds.length; i++) {
-      worlds[i] =
-          worldCollection
-              .find(Filters.eq(randomWorldNumber()))
-              .map(Helper::mongoDocumentToWorld)
-              .first();
-    }
-    sendJson(exchange, worlds);
-  }
-}

+ 0 - 76
frameworks/Java/undertow/src/main/java/hello/QueriesPgAsyncHandler.java

@@ -1,76 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendException;
-import static hello.Helper.sendJson;
-
-import io.reactiverse.pgclient.PgClient;
-import io.reactiverse.pgclient.PgResult;
-import io.reactiverse.pgclient.Row;
-import io.reactiverse.pgclient.Tuple;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import io.vertx.core.AsyncResult;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Handles the multi-query database test using PostgreSQL with an asynchronous
- * API.
- */
-final class QueriesPgAsyncHandler implements HttpHandler {
-  private final PgClient client;
-
-  QueriesPgAsyncHandler(PgClient client) {
-    this.client = Objects.requireNonNull(client);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    int queries = getQueries(exchange);
-    nWorlds(queries).whenComplete(
-        (worlds, exception) -> {
-          if (exception != null) {
-            sendException(exchange, exception);
-          } else {
-            sendJson(exchange, worlds);
-          }
-        });
-  }
-
-  private CompletableFuture<World[]> nWorlds(int n) {
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    CompletableFuture<World>[] futures = new CompletableFuture[n];
-    for (int i = 0; i < futures.length; i++) {
-      futures[i] = oneWorld();
-    }
-    return CompletableFuture.allOf(futures).thenApply(
-        nil -> {
-          World[] worlds = new World[futures.length];
-          for (int i = 0; i < futures.length; i++) {
-            worlds[i] = futures[i].join();
-          }
-          return worlds;
-        });
-  }
-
-  private CompletableFuture<World> oneWorld() {
-    CompletableFuture<World> future = new CompletableFuture<>();
-    client.preparedQuery(
-        "SELECT id, randomnumber FROM World WHERE id = $1",
-        Tuple.of(randomWorldNumber()),
-        (AsyncResult<PgResult<Row>> result) -> {
-          if (result.failed()) {
-            future.completeExceptionally(result.cause());
-          } else {
-            Row row = result.result().iterator().next();
-            int id = row.getInteger(0);
-            int randomNumber = row.getInteger(1);
-            World world = new World(id, randomNumber);
-            future.complete(world);
-          }
-        });
-    return future;
-  }
-}

+ 0 - 44
frameworks/Java/undertow/src/main/java/hello/QueriesSqlHandler.java

@@ -1,44 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendJson;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Objects;
-import javax.sql.DataSource;
-
-/**
- * Handles the multi-query database test using a SQL database.
- */
-final class QueriesSqlHandler implements HttpHandler {
-  private final DataSource db;
-
-  QueriesSqlHandler(DataSource db) {
-    this.db = Objects.requireNonNull(db);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) throws Exception {
-    int queries = getQueries(exchange);
-    World[] worlds = new World[queries];
-    try (Connection connection = db.getConnection();
-         PreparedStatement statement =
-             connection.prepareStatement("SELECT * FROM World WHERE id = ?")) {
-      for (int i = 0; i < worlds.length; i++) {
-        statement.setInt(1, randomWorldNumber());
-        try (ResultSet resultSet = statement.executeQuery()) {
-          resultSet.next();
-          int id = resultSet.getInt("id");
-          int randomNumber = resultSet.getInt("randomNumber");
-          worlds[i] = new World(id, randomNumber);
-        }
-      }
-    }
-    sendJson(exchange, worlds);
-  }
-}

+ 0 - 100
frameworks/Java/undertow/src/main/java/hello/UpdatesMongoAsyncHandler.java

@@ -1,100 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendException;
-import static hello.Helper.sendJson;
-
-import com.mongodb.async.client.MongoCollection;
-import com.mongodb.async.client.MongoDatabase;
-import com.mongodb.client.model.Filters;
-import com.mongodb.client.model.UpdateOneModel;
-import com.mongodb.client.model.Updates;
-import com.mongodb.client.model.WriteModel;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import org.bson.Document;
-import org.bson.conversions.Bson;
-
-/**
- * Handles the updates test using MongoDB with an asynchronous API.
- */
-final class UpdatesMongoAsyncHandler implements HttpHandler {
-  private final MongoCollection<Document> worldCollection;
-
-  UpdatesMongoAsyncHandler(MongoDatabase db) {
-    worldCollection = db.getCollection("world");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    int queries = getQueries(exchange);
-    nUpdatedWorlds(queries).whenComplete(
-        (worlds, exception) -> {
-          if (exception != null) {
-            sendException(exchange, exception);
-          } else {
-            sendJson(exchange, worlds);
-          }
-        });
-  }
-
-  private CompletableFuture<World[]> nUpdatedWorlds(int n) {
-    return nWorlds(n).thenCompose(
-        worlds -> {
-          List<WriteModel<Document>> writes = new ArrayList<>(worlds.length);
-          for (World world : worlds) {
-            world.randomNumber = randomWorldNumber();
-            Bson filter = Filters.eq(world.id);
-            Bson update = Updates.set("randomNumber", world.randomNumber);
-            writes.add(new UpdateOneModel<>(filter, update));
-          }
-          CompletableFuture<World[]> next = new CompletableFuture<>();
-          worldCollection.bulkWrite(
-              writes,
-              (result, exception) -> {
-                if (exception != null) {
-                  next.completeExceptionally(exception);
-                } else {
-                  next.complete(worlds);
-                }
-              });
-          return next;
-        });
-  }
-
-  private CompletableFuture<World[]> nWorlds(int n) {
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    CompletableFuture<World>[] futures = new CompletableFuture[n];
-    for (int i = 0; i < futures.length; i++) {
-      futures[i] = oneWorld();
-    }
-    return CompletableFuture.allOf(futures).thenApply(
-        nil -> {
-          World[] worlds = new World[futures.length];
-          for (int i = 0; i < futures.length; i++) {
-            worlds[i] = futures[i].join();
-          }
-          return worlds;
-        });
-  }
-
-  private CompletableFuture<World> oneWorld() {
-    CompletableFuture<World> future = new CompletableFuture<>();
-    worldCollection
-        .find(Filters.eq(randomWorldNumber()))
-        .map(Helper::mongoDocumentToWorld)
-        .first(
-            (world, exception) -> {
-              if (exception != null) {
-                future.completeExceptionally(exception);
-              } else {
-                future.complete(world);
-              }
-            });
-    return future;
-  }
-}

+ 0 - 51
frameworks/Java/undertow/src/main/java/hello/UpdatesMongoHandler.java

@@ -1,51 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendJson;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.client.model.Filters;
-import com.mongodb.client.model.UpdateOneModel;
-import com.mongodb.client.model.Updates;
-import com.mongodb.client.model.WriteModel;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.util.ArrayList;
-import java.util.List;
-import org.bson.Document;
-import org.bson.conversions.Bson;
-
-/**
- * Handles the updates test using MongoDB.
- */
-final class UpdatesMongoHandler implements HttpHandler {
-  private final MongoCollection<Document> worldCollection;
-
-  UpdatesMongoHandler(MongoDatabase db) {
-    worldCollection = db.getCollection("world");
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    int queries = getQueries(exchange);
-    World[] worlds = new World[queries];
-    for (int i = 0; i < worlds.length; i++) {
-      worlds[i] =
-          worldCollection
-              .find(Filters.eq(randomWorldNumber()))
-              .map(Helper::mongoDocumentToWorld)
-              .first();
-    }
-    List<WriteModel<Document>> writes = new ArrayList<>(worlds.length);
-    for (World world : worlds) {
-      world.randomNumber = randomWorldNumber();
-      Bson filter = Filters.eq(world.id);
-      Bson update = Updates.set("randomNumber", world.randomNumber);
-      writes.add(new UpdateOneModel<>(filter, update));
-    }
-    worldCollection.bulkWrite(writes);
-    sendJson(exchange, worlds);
-  }
-}

+ 0 - 100
frameworks/Java/undertow/src/main/java/hello/UpdatesPgAsyncHandler.java

@@ -1,100 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendException;
-import static hello.Helper.sendJson;
-
-import io.reactiverse.pgclient.PgClient;
-import io.reactiverse.pgclient.PgResult;
-import io.reactiverse.pgclient.Row;
-import io.reactiverse.pgclient.Tuple;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import io.vertx.core.AsyncResult;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Handles the updates test using PostgreSQL with an asynchronous API.
- */
-final class UpdatesPgAsyncHandler implements HttpHandler {
-  private final PgClient client;
-
-  UpdatesPgAsyncHandler(PgClient client) {
-    this.client = Objects.requireNonNull(client);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) {
-    int queries = getQueries(exchange);
-    nUpdatedWorlds(queries).whenComplete(
-        (worlds, exception) -> {
-          if (exception != null) {
-            sendException(exchange, exception);
-          } else {
-            sendJson(exchange, worlds);
-          }
-        });
-  }
-
-  private CompletableFuture<World[]> nUpdatedWorlds(int n) {
-    return nWorlds(n).thenCompose(
-        worlds -> {
-          List<Tuple> writes = new ArrayList<>(worlds.length);
-          for (World world : worlds) {
-            world.randomNumber = randomWorldNumber();
-            writes.add(Tuple.of(world.randomNumber, world.id));
-          }
-          CompletableFuture<World[]> next = new CompletableFuture<>();
-          client.preparedBatch(
-              "UPDATE world SET randomnumber = $1 WHERE id = $2",
-              writes,
-              (AsyncResult<PgResult<Row>> result) -> {
-                if (result.failed()) {
-                  next.completeExceptionally(result.cause());
-                } else {
-                  next.complete(worlds);
-                }
-              });
-          return next;
-        });
-  }
-
-  private CompletableFuture<World[]> nWorlds(int n) {
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    CompletableFuture<World>[] futures = new CompletableFuture[n];
-    for (int i = 0; i < futures.length; i++) {
-      futures[i] = oneWorld();
-    }
-    return CompletableFuture.allOf(futures).thenApply(
-        nil -> {
-          World[] worlds = new World[futures.length];
-          for (int i = 0; i < futures.length; i++) {
-            worlds[i] = futures[i].join();
-          }
-          return worlds;
-        });
-  }
-
-  private CompletableFuture<World> oneWorld() {
-    CompletableFuture<World> future = new CompletableFuture<>();
-    client.preparedQuery(
-        "SELECT id, randomnumber FROM World WHERE id = $1",
-        Tuple.of(randomWorldNumber()),
-        (AsyncResult<PgResult<Row>> result) -> {
-          if (result.failed()) {
-            future.completeExceptionally(result.cause());
-          } else {
-            Row row = result.result().iterator().next();
-            int id = row.getInteger(0);
-            int randomNumber = row.getInteger(1);
-            World world = new World(id, randomNumber);
-            future.complete(world);
-          }
-        });
-    return future;
-  }
-}

+ 0 - 56
frameworks/Java/undertow/src/main/java/hello/UpdatesSqlHandler.java

@@ -1,56 +0,0 @@
-package hello;
-
-import static hello.Helper.getQueries;
-import static hello.Helper.randomWorldNumber;
-import static hello.Helper.sendJson;
-
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Objects;
-import javax.sql.DataSource;
-
-/**
- * Handles the updates test using a SQL database.
- */
-final class UpdatesSqlHandler implements HttpHandler {
-  private final DataSource db;
-
-  UpdatesSqlHandler(DataSource db) {
-    this.db = Objects.requireNonNull(db);
-  }
-
-  @Override
-  public void handleRequest(HttpServerExchange exchange) throws Exception {
-    int queries = getQueries(exchange);
-    World[] worlds = new World[queries];
-    try (Connection connection = db.getConnection()) {
-      try (PreparedStatement statement =
-               connection.prepareStatement(
-                   "SELECT * FROM World WHERE id = ?")) {
-        for (int i = 0; i < worlds.length; i++) {
-          statement.setInt(1, randomWorldNumber());
-          try (ResultSet resultSet = statement.executeQuery()) {
-            resultSet.next();
-            int id = resultSet.getInt("id");
-            int randomNumber = resultSet.getInt("randomNumber");
-            worlds[i] = new World(id, randomNumber);
-          }
-        }
-      }
-      try (PreparedStatement statement =
-               connection.prepareStatement(
-                   "UPDATE World SET randomNumber = ? WHERE id = ?")) {
-        for (World world : worlds) {
-          world.randomNumber = randomWorldNumber();
-          statement.setInt(1, world.randomNumber);
-          statement.setInt(2, world.id);
-          statement.executeUpdate();
-        }
-      }
-    }
-    sendJson(exchange, worlds);
-  }
-}

+ 0 - 14
frameworks/Java/undertow/src/main/java/hello/World.java

@@ -1,14 +0,0 @@
-package hello;
-
-/**
- * The model for the "world" database table.
- */
-public final class World {
-  public int id;
-  public int randomNumber;
-
-  public World(int id, int randomNumber) {
-    this.id = id;
-    this.randomNumber = randomNumber;
-  }
-}

+ 0 - 10
frameworks/Java/undertow/undertow-mongodb-async.dockerfile

@@ -1,10 +0,0 @@
-FROM maven:3.5.3-jdk-10-slim as maven
-WORKDIR /undertow
-COPY pom.xml pom.xml
-COPY src src
-RUN mvn clean package -q
-
-FROM openjdk:10-jre-slim
-WORKDIR /undertow
-COPY --from=maven /undertow/target/app.jar app.jar
-CMD ["java", "-jar", "app.jar", "MONGODB_ASYNC"]

+ 0 - 10
frameworks/Java/undertow/undertow-mongodb.dockerfile

@@ -1,10 +0,0 @@
-FROM maven:3.5.3-jdk-10-slim as maven
-WORKDIR /undertow
-COPY pom.xml pom.xml
-COPY src src
-RUN mvn clean package -q
-
-FROM openjdk:10-jre-slim
-WORKDIR /undertow
-COPY --from=maven /undertow/target/app.jar app.jar
-CMD ["java", "-jar", "app.jar", "MONGODB"]

+ 0 - 10
frameworks/Java/undertow/undertow-mysql.dockerfile

@@ -1,10 +0,0 @@
-FROM maven:3.5.3-jdk-10-slim as maven
-WORKDIR /undertow
-COPY pom.xml pom.xml
-COPY src src
-RUN mvn clean package -q
-
-FROM openjdk:10-jre-slim
-WORKDIR /undertow
-COPY --from=maven /undertow/target/app.jar app.jar
-CMD ["java", "-jar", "app.jar", "MYSQL"]

+ 0 - 10
frameworks/Java/undertow/undertow-postgresql-async.dockerfile

@@ -1,10 +0,0 @@
-FROM maven:3.5.3-jdk-10-slim as maven
-WORKDIR /undertow
-COPY pom.xml pom.xml
-COPY src src
-RUN mvn clean package -q
-
-FROM openjdk:10-jre-slim
-WORKDIR /undertow
-COPY --from=maven /undertow/target/app.jar app.jar
-CMD ["java", "-jar", "app.jar", "POSTGRESQL_ASYNC"]