WebServer.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import org.vertx.java.core.Handler;
  2. import org.vertx.java.core.buffer.Buffer;
  3. import org.vertx.java.core.eventbus.Message;
  4. import org.vertx.java.core.http.HttpServerRequest;
  5. import org.vertx.java.core.http.HttpServerResponse;
  6. import org.vertx.java.core.json.JsonArray;
  7. import org.vertx.java.core.json.JsonObject;
  8. import org.vertx.java.core.json.impl.Json;
  9. import org.vertx.java.platform.Verticle;
  10. import java.text.DateFormat;
  11. import java.text.SimpleDateFormat;
  12. import java.util.Collections;
  13. import java.util.Date;
  14. import java.util.Random;
  15. import java.util.concurrent.ThreadLocalRandom;
  16. import freemarker.template.Template;
  17. import freemarker.template.Configuration;
  18. import java.io.StringReader;
  19. import java.io.Writer;
  20. import java.io.StringWriter;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.HashMap;
  24. import java.util.ArrayList;
  25. public class WebServer extends Verticle implements Handler<HttpServerRequest> {
  26. private final Buffer helloWorldBuffer = new Buffer("Hello, World!");
  27. private final String helloWorldContentLength = String.valueOf(helloWorldBuffer.length());
  28. private final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss z");
  29. private final Random random = ThreadLocalRandom.current();
  30. private String dateString;
  31. private static final String PATH_PLAINTEXT = "/plaintext";
  32. private static final String PATH_JSON = "/json";
  33. private static final String PATH_DB = "/db";
  34. private static final String PATH_QUERIES = "/queries";
  35. private static final String PATH_UPDATES = "/updates";
  36. private static final String PATH_FORTUNES = "/fortunes";
  37. private static final String RESPONSE_TYPE_PLAIN = "text/plain";
  38. private static final String RESPONSE_TYPE_HTML = "text/html";
  39. private static final String RESPONSE_TYPE_JSON = "application/json";
  40. private static final String HEADER_CONTENT_TYPE = "Content-Type";
  41. private static final String HEADER_CONTENT_LENGTH = "Content-Length";
  42. private static final String HEADER_SERVER = "Server";
  43. private static final String HEADER_SERVER_VERTX = "vert.x";
  44. private static final String HEADER_DATE = "Date";
  45. private static final String MONGO_ADDRESS = "hello.persistor";
  46. private static final String FREEMARKER_ADDRESS = "vertx.freemarker";
  47. private static final String UNDERSCORE_ID = "_id";
  48. private static final String TEXT_ID = "id";
  49. private static final String RANDOM_NUMBER = "randomNumber";
  50. private static final String TEXT_RESULT = "result";
  51. private static final String TEXT_RESULTS = "results";
  52. private static final String TEXT_QUERIES = "queries";
  53. private static final String TEXT_MESSAGE = "message";
  54. private static final String TEXT_MESSAGES = "messages";
  55. private static final String ADD_FORTUNE_MESSAGE = "Additional fortune added at request time.";
  56. private static final String HELLO_WORLD = "Hello, world!";
  57. private static final String TEXT_ACTION = "action";
  58. private static final String TEXT_CRITERIA = "criteria";
  59. private static final String TEXT_UPDATE = "update";
  60. private static final String TEXT_OBJ_NEW = "objNew";
  61. private static final String TEXT_FINDONE = "findone";
  62. private static final String TEXT_FIND = "find";
  63. private static final String TEXT_COLLECTION = "collection";
  64. private static final String TEXT_WORLD = "World";
  65. private static final String TEXT_FORTUNE = "Fortune";
  66. private static final String TEXT_MATCHER = "matcher";
  67. private static final String TEMPLATE_FORTUNE = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr><#list messages as message><tr><td>${message.id?html}</td><td>${message.message?html}</td></tr></#list></table></body></html>";
  68. private Template ftlTemplate;
  69. @Override
  70. public void start() {
  71. try { ftlTemplate = new Template(TEXT_FORTUNE, new StringReader(TEMPLATE_FORTUNE), new Configuration(Configuration.VERSION_2_3_22)); } catch (Exception ex) { ex.printStackTrace(); }
  72. vertx.createHttpServer().requestHandler(WebServer.this).listen(8080);
  73. vertx.setPeriodic(1000, new Handler<Long>() {
  74. @Override
  75. public void handle(Long timerID) {
  76. formatDate();
  77. }
  78. });
  79. formatDate();
  80. }
  81. @Override
  82. public void handle(HttpServerRequest req) {
  83. switch (req.path()) {
  84. case PATH_PLAINTEXT:
  85. handlePlainText(req);
  86. break;
  87. case PATH_JSON:
  88. handleJson(req);
  89. break;
  90. case PATH_DB:
  91. handleDbMongo(req);
  92. break;
  93. case PATH_QUERIES:
  94. handleDBMongo(req,false);
  95. break;
  96. case PATH_UPDATES:
  97. handleDBMongo(req,true);
  98. break;
  99. case PATH_FORTUNES:
  100. handleFortunes(req);
  101. break;
  102. default:
  103. req.response().setStatusCode(404);
  104. req.response().end();
  105. }
  106. }
  107. private void formatDate() {
  108. dateString = DATE_FORMAT.format(new Date());
  109. }
  110. private void handleFortunes(HttpServerRequest req) {
  111. final HttpServerResponse resp = req.response();
  112. vertx.eventBus().send(
  113. MONGO_ADDRESS,
  114. new JsonObject()
  115. .putString(TEXT_ACTION, TEXT_FIND)
  116. .putString(TEXT_COLLECTION, TEXT_FORTUNE),
  117. new Handler<Message<JsonObject>>() {
  118. @Override
  119. public void handle(Message<JsonObject> reply) {
  120. JsonArray results = reply.body().getArray(TEXT_RESULTS);
  121. List<Fortune> fortunes = new ArrayList<>();
  122. for (Object fortune: results) {
  123. fortunes.add(new Fortune(
  124. ((JsonObject)fortune).getNumber(TEXT_ID).intValue(),
  125. ((JsonObject)fortune).getString(TEXT_MESSAGE)));
  126. }
  127. fortunes.add(new Fortune(0, ADD_FORTUNE_MESSAGE));
  128. Collections.sort(fortunes);
  129. Map model = new HashMap();
  130. model.put(TEXT_MESSAGES, fortunes);
  131. Writer writer = new StringWriter();
  132. try { ftlTemplate.process(model, writer); } catch (Exception ex) { ex.printStackTrace(); }
  133. Buffer buff = new Buffer(writer.toString());
  134. setHeaders(resp, RESPONSE_TYPE_HTML, String.valueOf(buff.length()));
  135. resp.end(buff);
  136. }
  137. });
  138. }
  139. private void handlePlainText(HttpServerRequest req) {
  140. HttpServerResponse resp = req.response();
  141. setHeaders(resp, RESPONSE_TYPE_PLAIN, helloWorldContentLength);
  142. resp.end(helloWorldBuffer);
  143. }
  144. private void handleJson(HttpServerRequest req) {
  145. Buffer buff = new Buffer(Json.encode(Collections.singletonMap(TEXT_MESSAGE, HELLO_WORLD)));
  146. HttpServerResponse resp = req.response();
  147. setHeaders(resp, RESPONSE_TYPE_JSON, String.valueOf(buff.length()));
  148. resp.end(buff);
  149. }
  150. private void handleDbMongo(final HttpServerRequest req) {
  151. findRandom(new Handler<Message<JsonObject>>() {
  152. @Override
  153. public void handle(Message<JsonObject> reply) {
  154. JsonObject world = getResultFromReply(reply);
  155. String result = world.encode();
  156. sendResponse(req, result);
  157. }
  158. });
  159. }
  160. private JsonObject getResultFromReply(Message<JsonObject> reply) {
  161. JsonObject body = reply.body();
  162. JsonObject world = body.getObject(TEXT_RESULT);
  163. Object id = world.removeField(UNDERSCORE_ID);
  164. world.putValue(TEXT_ID, Integer.valueOf(((Double)id).intValue()));
  165. return world;
  166. }
  167. private void handleDBMongo(final HttpServerRequest req, boolean randomUpdates) {
  168. int queriesParam = 1;
  169. try {
  170. queriesParam = Integer.parseInt(req.params().get(TEXT_QUERIES));
  171. } catch (NumberFormatException e) {
  172. queriesParam = 1;
  173. }
  174. if (queriesParam < 1) {
  175. queriesParam = 1;
  176. } else if (queriesParam > 500) {
  177. queriesParam = 500;
  178. }
  179. final MongoHandler dbh = new MongoHandler(req, queriesParam, randomUpdates);
  180. for (int i = 0; i < queriesParam; i++) {
  181. findRandom(dbh);
  182. }
  183. }
  184. private void findRandom(Handler<Message<JsonObject>> handler) {
  185. vertx.eventBus().send(
  186. MONGO_ADDRESS,
  187. new JsonObject()
  188. .putString(TEXT_ACTION, TEXT_FINDONE)
  189. .putString(TEXT_COLLECTION, TEXT_WORLD)
  190. .putObject(TEXT_MATCHER, new JsonObject().putNumber(UNDERSCORE_ID, (random.nextInt(10000) + 1))),
  191. handler);
  192. }
  193. private void updateRandom(JsonObject json) {
  194. vertx.eventBus().send(
  195. MONGO_ADDRESS,
  196. new JsonObject()
  197. .putString(TEXT_ACTION, TEXT_UPDATE)
  198. .putString(TEXT_COLLECTION, TEXT_WORLD)
  199. .putObject(TEXT_CRITERIA, new JsonObject().putValue(UNDERSCORE_ID, json.getValue(TEXT_ID)))
  200. .putObject(TEXT_OBJ_NEW, json)
  201. );
  202. }
  203. private void sendResponse(HttpServerRequest req, String result) {
  204. Buffer buff = new Buffer(result);
  205. HttpServerResponse resp = req.response();
  206. setHeaders(resp, RESPONSE_TYPE_JSON, String.valueOf(buff.length()));
  207. resp.end(buff);
  208. }
  209. private void setHeaders(HttpServerResponse resp, String contentType, String contentLength) {
  210. resp.putHeader(HEADER_CONTENT_TYPE, contentType);
  211. resp.putHeader(HEADER_CONTENT_LENGTH, contentLength);
  212. resp.putHeader(HEADER_SERVER, HEADER_SERVER_VERTX );
  213. resp.putHeader(HEADER_DATE, dateString);
  214. }
  215. private final class MongoHandler implements Handler<Message<JsonObject>> {
  216. private final HttpServerRequest req;
  217. private final int queries;
  218. private final JsonArray worlds;
  219. private final Random random;
  220. private final boolean randomUpdates;
  221. public MongoHandler(HttpServerRequest request, int queriesParam, boolean performRandomUpdates) {
  222. req = request;
  223. queries = queriesParam;
  224. randomUpdates = performRandomUpdates;
  225. random = ThreadLocalRandom.current();
  226. worlds = new JsonArray();
  227. }
  228. @Override
  229. public void handle(Message<JsonObject> reply) {
  230. JsonObject world = getResultFromReply(reply);
  231. if (randomUpdates) {
  232. world.putValue(RANDOM_NUMBER, (random.nextInt(10000) + 1));
  233. updateRandom(world);
  234. }
  235. worlds.add(world);
  236. if (worlds.size() == this.queries) {
  237. // All queries have completed; send the response.
  238. String result = worlds.encode();
  239. sendResponse(req, result);
  240. }
  241. }
  242. }
  243. public final class Fortune implements Comparable<Fortune> {
  244. public int id;
  245. public String message;
  246. public int getId() {
  247. return id;
  248. }
  249. public String getMessage() {
  250. return message;
  251. }
  252. public Fortune(int id, String message) {
  253. this.id = id;
  254. this.message = message;
  255. }
  256. @Override
  257. public int compareTo(Fortune other) {
  258. return message.compareTo(other.message);
  259. }
  260. }
  261. }