server.dart 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import "dart:core";
  2. import "dart:io";
  3. import "dart:isolate";
  4. import 'dart:async' show Future;
  5. import 'dart:math' show Random;
  6. import "package:redstone/server.dart" as app;
  7. import "package:redstone_mapper/mapper.dart";
  8. import "package:redstone_mapper/plugin.dart";
  9. import "package:redstone_mapper_mongo/manager.dart";
  10. import "package:redstone_mapper_pg/manager.dart";
  11. import "package:postgresql/postgresql.dart" as pg;
  12. import "package:di/di.dart";
  13. import "package:args/args.dart";
  14. import 'package:yaml/yaml.dart' as yaml;
  15. import 'package:mustache/mustache.dart' as mustache;
  16. const _WORLD_TABLE_SIZE = 10000;
  17. final _RANDOM = new Random();
  18. class Fortune implements Comparable<Fortune> {
  19. @Field()
  20. int id;
  21. @Field()
  22. String message;
  23. Fortune([this.id, this.message]);
  24. compareTo(Fortune other) => message.compareTo(other.message);
  25. }
  26. class World {
  27. @Field()
  28. int id;
  29. @Field(model: "randomnumber")
  30. int randomNumber;
  31. }
  32. class MongoFortune implements Comparable<MongoFortune> {
  33. int _id;
  34. @Field(model: "_id")
  35. int get id => _id;
  36. @Field(model: "_id")
  37. set id(num value) => _id = value.toInt();
  38. @Field()
  39. String message;
  40. MongoFortune([this._id, this.message]);
  41. compareTo(MongoFortune other) => message.compareTo(other.message);
  42. }
  43. class MongoWorld {
  44. int _id;
  45. int _randomNumber;
  46. @Field(model: "_id")
  47. int get id => _id;
  48. @Field(model: "_id")
  49. set id(num value) => _id = value.toInt();
  50. @Field()
  51. int get randomNumber => _randomNumber;
  52. @Field()
  53. set randomNumber(num value) => _randomNumber = value.toInt();
  54. }
  55. ///Handle PostgreSql connections
  56. @app.Interceptor(r'/pg/.+')
  57. pgSqlManager(PostgreSqlManager pgSql) {
  58. pgSql.getConnection().then((conn) {
  59. app.request.attributes["dbConn"] = conn;
  60. app.chain.next(() {
  61. pgSql.closeConnection(conn, error: app.chain.error);
  62. });
  63. });
  64. }
  65. ///Handle MongoDb connections
  66. @app.Interceptor(r'/mongo/.+')
  67. mongoDbManager(MongoDbManager mongoDb) {
  68. mongoDb.getConnection().then((conn) {
  69. app.request.attributes["dbConn"] = conn;
  70. app.chain.next(() {
  71. mongoDb.closeConnection(conn, error: app.chain.error);
  72. });
  73. });
  74. }
  75. ///JSON test
  76. @app.Route("/json")
  77. getJson() => {"message": "Hello, World!"};
  78. ///PlainText test
  79. @app.Route("/plaintext")
  80. getPlainText() => "Hello, World!";
  81. ///PostgreSql tests
  82. @app.Group("/pg")
  83. @Encode()
  84. class PgTests {
  85. static const worldQuery = 'SELECT id, randomnumber FROM world WHERE id = @id;';
  86. static const worldUpdt = 'UPDATE world SET randomnumber = @randomnumber WHERE id = @id;';
  87. static const fortuneQuery = 'SELECT id, message FROM fortune;';
  88. PostgreSql get pgSql => app.request.attributes["dbConn"];
  89. @app.Route("/db")
  90. Future<World> queryTest() {
  91. var params = { 'id': _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1 };
  92. return pgSql.query(worldQuery, World, params).then((list) => list[0]);
  93. }
  94. @app.Route("/queries")
  95. Future<List<World>> queriesTest() {
  96. var queries = _parseQueriesParam(app.request.queryParams.queries);
  97. return Future.wait(new List.generate(queries, (_) => queryTest()));
  98. }
  99. @app.Route("/updates")
  100. Future<List<World>> updateTest() {
  101. var queries = _parseQueriesParam(app.request.queryParams.queries);
  102. return Future.wait(new List.generate(queries, (_) => queryTest().then((world) {
  103. world.randomNumber = _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1;
  104. return pgSql.execute(worldUpdt, world).then((_) => world);
  105. })));
  106. }
  107. @app.Route("/fortunes", responseType: "text/html")
  108. Future<String> fortunesTest(@app.Inject() mustache.Template template) {
  109. return pgSql.query(fortuneQuery, Fortune).then((values) {
  110. values
  111. ..add(new Fortune(0, 'Additional fortune added at request time.'))
  112. ..sort();
  113. return template.renderString({
  114. "fortunes": encode(values)
  115. });
  116. });
  117. }
  118. }
  119. ///MongoDb tests
  120. @app.Group("/mongo")
  121. @Encode()
  122. class MongoTests {
  123. static const worldCollection = "World";
  124. static const fortuneCollection = "Fortune";
  125. MongoDb get mongoDb => app.request.attributes["dbConn"];
  126. @app.Route("/db")
  127. Future<MongoWorld> queryTest() {
  128. return mongoDb.findOne(worldCollection, MongoWorld, {
  129. "_id": _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1
  130. });
  131. }
  132. @app.Route("/queries")
  133. Future<List<MongoWorld>> queriesTest() {
  134. var queries = _parseQueriesParam(app.request.queryParams.queries);
  135. return Future.wait(new List.generate(queries, (_) => queryTest()));
  136. }
  137. @app.Route("/updates")
  138. Future<List<MongoWorld>> updateTest() {
  139. var queries = _parseQueriesParam(app.request.queryParams.queries);
  140. return Future.wait(new List.generate(queries, (_) => queryTest().then((world) {
  141. world.randomNumber = _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1;
  142. return mongoDb.update(worldCollection, { "_id": world.id }, world)
  143. .then((_) => world);
  144. })));
  145. }
  146. @app.Route("/fortunes", responseType: "text/html")
  147. Future<String> fortunesTest(@app.Inject() mustache.Template template) {
  148. return mongoDb.find(fortuneCollection, MongoFortune).then((values) {
  149. values
  150. ..add(new MongoFortune(0, 'Additional fortune added at request time.'))
  151. ..sort();
  152. return template.renderString({
  153. "fortunes": encode(values)
  154. });
  155. });
  156. }
  157. }
  158. main(List<String> args) {
  159. var parser = new ArgParser();
  160. parser.addOption('address', abbr: 'a', defaultsTo: '0.0.0.0');
  161. parser.addOption('port', abbr: 'p', defaultsTo: '8080');
  162. parser.addOption('dbconnections', abbr: 'd', defaultsTo: '256');
  163. parser.addOption('isolates', abbr: 'i', defaultsTo: '1');
  164. var arguments = parser.parse(args);
  165. var isolates = int.parse(arguments['isolates']);
  166. var dbConnections = int.parse(arguments['dbconnections']) ~/ isolates;
  167. ServerSocket.bind(arguments['address'], int.parse(arguments['port']))
  168. .then((server) {
  169. var ref = server.reference;
  170. for (int i = 1; i < isolates; i++) {
  171. Isolate.spawn(startInIsolate, [ref, dbConnections]);
  172. }
  173. _startServer(server, dbConnections);
  174. });
  175. }
  176. void startInIsolate(args) {
  177. var ref = args[0];
  178. var dbConnections = args[1];
  179. ref.create().then((server) {
  180. _startServer(server, dbConnections);
  181. });
  182. }
  183. _startServer(serverSocket, dbConnections) {
  184. MongoDbManager mongoDbManager;
  185. PostgreSqlManager pgSqlManager;
  186. mustache.Template fortunesTemplate;
  187. Future.wait([
  188. //load PostgreSql configuration
  189. new File("postgresql.yaml").readAsString().then((config){
  190. pgSqlManager = new PostgreSqlManager(
  191. new pg.Settings.fromMap(yaml.loadYaml(config)).toUri(),
  192. min: dbConnections,
  193. max: dbConnections);
  194. }),
  195. //load MongoDb configuration
  196. new File("mongodb.yaml").readAsString().then((config) {
  197. var mongoConfig = yaml.loadYaml(config);
  198. mongoDbManager = new MongoDbManager(
  199. "mongodb://${mongoConfig["host"]}/${mongoConfig["database"]}",
  200. poolSize: dbConnections);
  201. }),
  202. //load fortunes mustache template
  203. new File('fortunes.mustache').readAsString().then((template) {
  204. fortunesTemplate = mustache.parse(template);
  205. })
  206. ]).then((_) {
  207. //app.setupConsoleLog();
  208. //install module for dependency injection
  209. app.addModule(new Module()
  210. ..bind(MongoDbManager, toValue: mongoDbManager)
  211. ..bind(PostgreSqlManager, toValue: pgSqlManager)
  212. ..bind(mustache.Template, toValue: fortunesTemplate));
  213. //initialize mapper plugin
  214. app.addPlugin(getMapperPlugin());
  215. //start the server
  216. var server = new HttpServer.listenOn(serverSocket);
  217. app.serveRequests(server);
  218. });
  219. }
  220. _parseQueriesParam(param) {
  221. return param == null || param.isEmpty ? 1 :
  222. int.parse(param, radix: 10, onError: (_) => 1).clamp(1, 500);
  223. }