HelloWebServer.java 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package hello;
  2. import com.mongodb.MongoClient;
  3. import com.mongodb.MongoClientOptions;
  4. import com.mongodb.ServerAddress;
  5. import com.mongodb.async.client.MongoClientSettings;
  6. import com.mongodb.async.client.MongoClients;
  7. import com.mongodb.client.MongoDatabase;
  8. import com.mongodb.connection.ClusterConnectionMode;
  9. import com.mongodb.connection.ClusterSettings;
  10. import com.mongodb.connection.ConnectionPoolSettings;
  11. import com.zaxxer.hikari.HikariConfig;
  12. import com.zaxxer.hikari.HikariDataSource;
  13. import io.undertow.Undertow;
  14. import io.undertow.UndertowOptions;
  15. import io.undertow.server.HttpHandler;
  16. import io.undertow.server.handlers.BlockingHandler;
  17. import io.undertow.server.handlers.PathHandler;
  18. import io.undertow.server.handlers.SetHeaderHandler;
  19. import java.io.InputStream;
  20. import java.util.Collections;
  21. import java.util.Properties;
  22. import javax.sql.DataSource;
  23. /**
  24. * Provides the {@link #main(String[])} method, which launches the application.
  25. */
  26. public final class HelloWebServer {
  27. private HelloWebServer() {
  28. throw new AssertionError();
  29. }
  30. public static void main(String[] args) throws Exception {
  31. Mode mode = Mode.valueOf(args[0]);
  32. Properties props = new Properties();
  33. try (InputStream in =
  34. Thread.currentThread()
  35. .getContextClassLoader()
  36. .getResourceAsStream("hello/server.properties")) {
  37. props.load(in);
  38. }
  39. int port = Integer.parseInt(props.getProperty("undertow.port"));
  40. String host = props.getProperty("undertow.host");
  41. HttpHandler paths = mode.paths(props);
  42. HttpHandler rootHandler = new SetHeaderHandler(paths, "Server", "U-tow");
  43. Undertow.builder()
  44. .addHttpListener(port, host)
  45. // In HTTP/1.1, connections are persistent unless declared
  46. // otherwise. Adding a "Connection: keep-alive" header to every
  47. // response would only add useless bytes.
  48. .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
  49. .setHandler(rootHandler)
  50. .build()
  51. .start();
  52. }
  53. enum Mode {
  54. /**
  55. * The server will only implement the test types that do not require a
  56. * database.
  57. */
  58. NO_DATABASE() {
  59. @Override
  60. HttpHandler paths(Properties props) {
  61. return new PathHandler()
  62. .addExactPath("/plaintext", new PlaintextHandler())
  63. .addExactPath("/json", new JsonHandler());
  64. }
  65. },
  66. /**
  67. * The server will use a MySQL database and will only implement the test
  68. * types that require a database.
  69. */
  70. MYSQL() {
  71. @Override
  72. HttpHandler paths(Properties props) {
  73. String jdbcUrl = props.getProperty("mysql.jdbcUrl");
  74. String username = props.getProperty("mysql.username");
  75. String password = props.getProperty("mysql.password");
  76. int connections = Integer.parseInt(props.getProperty("mysql.connections"));
  77. DataSource db = newSqlDataSource(jdbcUrl, username, password, connections);
  78. return new PathHandler()
  79. .addExactPath("/db", new BlockingHandler(new DbSqlHandler(db)))
  80. .addExactPath("/queries", new BlockingHandler(new QueriesSqlHandler(db)))
  81. .addExactPath("/fortunes", new BlockingHandler(new FortunesSqlHandler(db)))
  82. .addExactPath("/updates", new BlockingHandler(new UpdatesSqlHandler(db)));
  83. }
  84. },
  85. /**
  86. * The server will use a PostgreSQL database and will only implement the
  87. * test types that require a database.
  88. */
  89. POSTGRESQL() {
  90. @Override
  91. HttpHandler paths(Properties props) {
  92. String jdbcUrl = props.getProperty("postgresql.jdbcUrl");
  93. String username = props.getProperty("postgresql.username");
  94. String password = props.getProperty("postgresql.password");
  95. int connections = Integer.parseInt(props.getProperty("postgresql.connections"));
  96. DataSource db = newSqlDataSource(jdbcUrl, username, password, connections);
  97. return new PathHandler()
  98. .addExactPath("/db", new BlockingHandler(new DbSqlHandler(db)))
  99. .addExactPath("/queries", new BlockingHandler(new QueriesSqlHandler(db)))
  100. .addExactPath("/fortunes", new BlockingHandler(new FortunesSqlHandler(db)))
  101. .addExactPath("/updates", new BlockingHandler(new UpdatesSqlHandler(db)));
  102. }
  103. },
  104. /**
  105. * The server will use a MongoDB database and will only implement the test
  106. * types that require a database.
  107. */
  108. MONGODB() {
  109. @Override
  110. HttpHandler paths(Properties props) {
  111. String host = props.getProperty("mongodb.host");
  112. String databaseName = props.getProperty("mongodb.databaseName");
  113. int connections = Integer.parseInt(props.getProperty("mongodb.connections"));
  114. MongoDatabase db = newMongoDatabase(host, databaseName, connections);
  115. return new PathHandler()
  116. .addExactPath("/db", new BlockingHandler(new DbMongoHandler(db)))
  117. .addExactPath("/queries", new BlockingHandler(new QueriesMongoHandler(db)))
  118. .addExactPath("/fortunes", new BlockingHandler(new FortunesMongoHandler(db)))
  119. .addExactPath("/updates", new BlockingHandler(new UpdatesMongoHandler(db)));
  120. }
  121. },
  122. /**
  123. * The server will use a MongoDB database with an asynchronous API and will
  124. * only implement the test types that require a database.
  125. */
  126. MONGODB_ASYNC() {
  127. @Override
  128. HttpHandler paths(Properties props) {
  129. String host = props.getProperty("mongodb.host");
  130. String databaseName = props.getProperty("mongodb.databaseName");
  131. int connections = Integer.parseInt(props.getProperty("mongodb.connections"));
  132. com.mongodb.async.client.MongoDatabase db =
  133. newMongoDatabaseAsync(host, databaseName, connections);
  134. return new PathHandler()
  135. .addExactPath("/db", new AsyncHandler(new DbMongoAsyncHandler(db)))
  136. .addExactPath("/queries", new AsyncHandler(new QueriesMongoAsyncHandler(db)))
  137. .addExactPath("/fortunes", new AsyncHandler(new FortunesMongoAsyncHandler(db)))
  138. .addExactPath("/updates", new AsyncHandler(new UpdatesMongoAsyncHandler(db)));
  139. }
  140. };
  141. /**
  142. * Returns an HTTP handler that provides routing for all the
  143. * test-type-specific endpoints of the server.
  144. *
  145. * @param props the server configuration
  146. */
  147. abstract HttpHandler paths(Properties props);
  148. /**
  149. * Provides a source of connections to a SQL database.
  150. */
  151. static DataSource newSqlDataSource(String jdbcUrl,
  152. String username,
  153. String password,
  154. int connections) {
  155. HikariConfig config = new HikariConfig();
  156. config.setJdbcUrl(jdbcUrl);
  157. config.setUsername(username);
  158. config.setPassword(password);
  159. config.setMaximumPoolSize(connections);
  160. return new HikariDataSource(config);
  161. }
  162. /**
  163. * Provides a source of connections to a MongoDB database.
  164. */
  165. static MongoDatabase newMongoDatabase(String host,
  166. String databaseName,
  167. int connections) {
  168. MongoClientOptions.Builder options = MongoClientOptions.builder();
  169. options.connectionsPerHost(connections);
  170. options.threadsAllowedToBlockForConnectionMultiplier(
  171. (int) Math.ceil((double) MAX_DB_REQUEST_CONCURRENCY / connections));
  172. MongoClient client = new MongoClient(host, options.build());
  173. return client.getDatabase(databaseName);
  174. }
  175. /**
  176. * Provides a source of connections to a MongoDB database with an
  177. * asynchronous API.
  178. */
  179. static com.mongodb.async.client.MongoDatabase
  180. newMongoDatabaseAsync(String host,
  181. String databaseName,
  182. int connections) {
  183. ClusterSettings clusterSettings =
  184. ClusterSettings
  185. .builder()
  186. .mode(ClusterConnectionMode.SINGLE)
  187. .hosts(Collections.singletonList(new ServerAddress(host)))
  188. .build();
  189. ConnectionPoolSettings connectionPoolSettings =
  190. ConnectionPoolSettings
  191. .builder()
  192. .maxSize(connections)
  193. .maxWaitQueueSize(
  194. MAX_DB_REQUEST_CONCURRENCY * MAX_DB_QUERIES_PER_REQUEST)
  195. .build();
  196. MongoClientSettings clientSettings =
  197. MongoClientSettings
  198. .builder()
  199. .clusterSettings(clusterSettings)
  200. .connectionPoolSettings(connectionPoolSettings)
  201. .build();
  202. com.mongodb.async.client.MongoClient client =
  203. MongoClients.create(clientSettings);
  204. return client.getDatabase(databaseName);
  205. }
  206. private static final int MAX_DB_REQUEST_CONCURRENCY = 256;
  207. private static final int MAX_DB_QUERIES_PER_REQUEST = 20;
  208. }
  209. }