application.js 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. /*!
  2. * Pomelo -- proto
  3. * Copyright(c) 2012 xiechengchao <xiecc@163.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var utils = require('./util/utils');
  10. var logger = require('pomelo-logger').getLogger('pomelo', __filename);
  11. var EventEmitter = require('events').EventEmitter;
  12. var events = require('./util/events');
  13. var appUtil = require('./util/appUtil');
  14. var Constants = require('./util/constants');
  15. var appManager = require('./common/manager/appManager');
  16. var fs = require('fs');
  17. var path = require('path');
  18. /**
  19. * Application prototype.
  20. *
  21. * @module
  22. */
  23. var Application = module.exports = {};
  24. /**
  25. * Application states
  26. */
  27. var STATE_INITED = 1; // app has inited
  28. var STATE_START = 2; // app start
  29. var STATE_STARTED = 3; // app has started
  30. var STATE_STOPED = 4; // app has stoped
  31. /**
  32. * Initialize the server.
  33. *
  34. * - setup default configuration
  35. */
  36. Application.init = function(opts) {
  37. opts = opts || {};
  38. this.loaded = []; // loaded component list
  39. this.components = {}; // name -> component map
  40. this.settings = {}; // collection keep set/get
  41. var base = opts.base || path.dirname(require.main.filename);
  42. this.set(Constants.RESERVED.BASE, base, true);
  43. this.event = new EventEmitter(); // event object to sub/pub events
  44. // current server info
  45. this.serverId = null; // current server id
  46. this.serverType = null; // current server type
  47. this.curServer = null; // current server info
  48. this.startTime = null; // current server start time
  49. // global server infos
  50. this.master = null; // master server info
  51. this.servers = {}; // current global server info maps, id -> info
  52. this.serverTypeMaps = {}; // current global type maps, type -> [info]
  53. this.serverTypes = []; // current global server type list
  54. this.lifecycleCbs = {}; // current server custom lifecycle callbacks
  55. this.clusterSeq = {}; // cluster id seqence
  56. appUtil.defaultConfiguration(this);
  57. this.state = STATE_INITED;
  58. logger.info('application inited: %j', this.getServerId());
  59. };
  60. /**
  61. * Get application base path
  62. *
  63. * // cwd: /home/game/
  64. * pomelo start
  65. * // app.getBase() -> /home/game
  66. *
  67. * @return {String} application base path
  68. *
  69. * @memberOf Application
  70. */
  71. Application.getBase = function() {
  72. return this.get(Constants.RESERVED.BASE);
  73. };
  74. /**
  75. * Override require method in application
  76. *
  77. * @param {String} relative path of file
  78. *
  79. * @memberOf Application
  80. */
  81. Application.require = function(ph) {
  82. return require(path.join(Application.getBase(), ph));
  83. };
  84. /**
  85. * Configure logger with {$base}/config/log4js.json
  86. *
  87. * @param {Object} logger pomelo-logger instance without configuration
  88. *
  89. * @memberOf Application
  90. */
  91. Application.configureLogger = function(logger) {
  92. if (process.env.POMELO_LOGGER !== 'off') {
  93. var base = this.getBase();
  94. var env = this.get(Constants.RESERVED.ENV);
  95. var originPath = path.join(base, Constants.FILEPATH.LOG);
  96. var presentPath = path.join(base, Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.LOG));
  97. if(fs.existsSync(originPath)) {
  98. logger.configure(originPath, {serverId: this.serverId, base: base});
  99. } else if(fs.existsSync(presentPath)) {
  100. logger.configure(presentPath, {serverId: this.serverId, base: base});
  101. } else {
  102. logger.error('logger file path configuration is error.');
  103. }
  104. }
  105. };
  106. /**
  107. * add a filter to before and after filter
  108. *
  109. * @param {Object} filter provide before and after filter method.
  110. * A filter should have two methods: before and after.
  111. * @memberOf Application
  112. */
  113. Application.filter = function (filter) {
  114. this.before(filter);
  115. this.after(filter);
  116. };
  117. /**
  118. * Add before filter.
  119. *
  120. * @param {Object|Function} bf before fileter, bf(msg, session, next)
  121. * @memberOf Application
  122. */
  123. Application.before = function (bf) {
  124. addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf);
  125. };
  126. /**
  127. * Add after filter.
  128. *
  129. * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`
  130. * @memberOf Application
  131. */
  132. Application.after = function (af) {
  133. addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af);
  134. };
  135. /**
  136. * add a global filter to before and after global filter
  137. *
  138. * @param {Object} filter provide before and after filter method.
  139. * A filter should have two methods: before and after.
  140. * @memberOf Application
  141. */
  142. Application.globalFilter = function (filter) {
  143. this.globalBefore(filter);
  144. this.globalAfter(filter);
  145. };
  146. /**
  147. * Add global before filter.
  148. *
  149. * @param {Object|Function} bf before fileter, bf(msg, session, next)
  150. * @memberOf Application
  151. */
  152. Application.globalBefore = function (bf) {
  153. addFilter(this, Constants.KEYWORDS.GLOBAL_BEFORE_FILTER, bf);
  154. };
  155. /**
  156. * Add global after filter.
  157. *
  158. * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`
  159. * @memberOf Application
  160. */
  161. Application.globalAfter = function (af) {
  162. addFilter(this, Constants.KEYWORDS.GLOBAL_AFTER_FILTER, af);
  163. };
  164. /**
  165. * Add rpc before filter.
  166. *
  167. * @param {Object|Function} bf before fileter, bf(serverId, msg, opts, next)
  168. * @memberOf Application
  169. */
  170. Application.rpcBefore = function(bf) {
  171. addFilter(this, Constants.KEYWORDS.RPC_BEFORE_FILTER, bf);
  172. };
  173. /**
  174. * Add rpc after filter.
  175. *
  176. * @param {Object|Function} af after filter, `af(serverId, msg, opts, next)`
  177. * @memberOf Application
  178. */
  179. Application.rpcAfter = function(af) {
  180. addFilter(this, Constants.KEYWORDS.RPC_AFTER_FILTER, af);
  181. };
  182. /**
  183. * add a rpc filter to before and after rpc filter
  184. *
  185. * @param {Object} filter provide before and after filter method.
  186. * A filter should have two methods: before and after.
  187. * @memberOf Application
  188. */
  189. Application.rpcFilter = function(filter) {
  190. this.rpcBefore(filter);
  191. this.rpcAfter(filter);
  192. };
  193. /**
  194. * Load component
  195. *
  196. * @param {String} name (optional) name of the component
  197. * @param {Object} component component instance or factory function of the component
  198. * @param {[type]} opts (optional) construct parameters for the factory function
  199. * @return {Object} app instance for chain invoke
  200. * @memberOf Application
  201. */
  202. Application.load = function(name, component, opts) {
  203. if(typeof name !== 'string') {
  204. opts = component;
  205. component = name;
  206. name = null;
  207. if(typeof component.name === 'string') {
  208. name = component.name;
  209. }
  210. }
  211. if(typeof component === 'function') {
  212. component = component(this, opts);
  213. }
  214. if(!name && typeof component.name === 'string') {
  215. name = component.name;
  216. }
  217. if(name && this.components[name]) {
  218. // ignore duplicat component
  219. logger.warn('ignore duplicate component: %j', name);
  220. return;
  221. }
  222. this.loaded.push(component);
  223. if(name) {
  224. // components with a name would get by name throught app.components later.
  225. this.components[name] = component;
  226. }
  227. return this;
  228. };
  229. /**
  230. * Load Configure json file to settings.(support different enviroment directory & compatible for old path)
  231. *
  232. * @param {String} key environment key
  233. * @param {String} val environment value
  234. * @param {Boolean} reload whether reload after change default false
  235. * @return {Server|Mixed} for chaining, or the setting value
  236. * @memberOf Application
  237. */
  238. Application.loadConfigBaseApp = function (key, val, reload) {
  239. var self = this;
  240. var env = this.get(Constants.RESERVED.ENV);
  241. var originPath = path.join(Application.getBase(), val);
  242. var presentPath = path.join(Application.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(val));
  243. var realPath;
  244. if(fs.existsSync(originPath)) {
  245. realPath = originPath;
  246. var file = require(originPath);
  247. if (file[env]) {
  248. file = file[env];
  249. }
  250. this.set(key, file);
  251. } else if(fs.existsSync(presentPath)) {
  252. realPath = presentPath;
  253. var pfile = require(presentPath);
  254. this.set(key, pfile);
  255. } else {
  256. logger.error('invalid configuration with file path: %s', key);
  257. }
  258. if(!!realPath && !!reload) {
  259. fs.watch(realPath, function (event, filename) {
  260. if(event === 'change') {
  261. delete require.cache[require.resolve(realPath)]
  262. self.loadConfigBaseApp(key, val);
  263. }
  264. });
  265. }
  266. };
  267. /**
  268. * Load Configure json file to settings.
  269. *
  270. * @param {String} key environment key
  271. * @param {String} val environment value
  272. * @return {Server|Mixed} for chaining, or the setting value
  273. * @memberOf Application
  274. */
  275. Application.loadConfig = function(key, val) {
  276. var env = this.get(Constants.RESERVED.ENV);
  277. val = require(val);
  278. if (val[env]) {
  279. val = val[env];
  280. }
  281. this.set(key, val);
  282. };
  283. /**
  284. * Set the route function for the specified server type.
  285. *
  286. * Examples:
  287. *
  288. * app.route('area', routeFunc);
  289. *
  290. * var routeFunc = function(session, msg, app, cb) {
  291. * // all request to area would be route to the first area server
  292. * var areas = app.getServersByType('area');
  293. * cb(null, areas[0].id);
  294. * };
  295. *
  296. * @param {String} serverType server type string
  297. * @param {Function} routeFunc route function. routeFunc(session, msg, app, cb)
  298. * @return {Object} current application instance for chain invoking
  299. * @memberOf Application
  300. */
  301. Application.route = function(serverType, routeFunc) {
  302. var routes = this.get(Constants.KEYWORDS.ROUTE);
  303. if(!routes) {
  304. routes = {};
  305. this.set(Constants.KEYWORDS.ROUTE, routes);
  306. }
  307. routes[serverType] = routeFunc;
  308. return this;
  309. };
  310. /**
  311. * Set before stop function. It would perform before servers stop.
  312. *
  313. * @param {Function} fun before close function
  314. * @return {Void}
  315. * @memberOf Application
  316. */
  317. Application.beforeStopHook = function(fun) {
  318. logger.warn('this method was deprecated in pomelo 0.8');
  319. if(!!fun && typeof fun === 'function') {
  320. this.set(Constants.KEYWORDS.BEFORE_STOP_HOOK, fun);
  321. }
  322. };
  323. /**
  324. * Start application. It would load the default components and start all the loaded components.
  325. *
  326. * @param {Function} cb callback function
  327. * @memberOf Application
  328. */
  329. Application.start = function(cb) {
  330. this.startTime = Date.now();
  331. if(this.state > STATE_INITED) {
  332. utils.invokeCallback(cb, new Error('application has already start.'));
  333. return;
  334. }
  335. var self = this;
  336. appUtil.startByType(self, function() {
  337. appUtil.loadDefaultComponents(self);
  338. var startUp = function() {
  339. appUtil.optComponents(self.loaded, Constants.RESERVED.START, function(err) {
  340. self.state = STATE_START;
  341. if(err) {
  342. utils.invokeCallback(cb, err);
  343. } else {
  344. logger.info('%j enter after start...', self.getServerId());
  345. self.afterStart(cb);
  346. }
  347. });
  348. };
  349. var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP];
  350. if(!!beforeFun) {
  351. beforeFun.call(null, self, startUp);
  352. } else {
  353. startUp();
  354. }
  355. });
  356. };
  357. /**
  358. * Lifecycle callback for after start.
  359. *
  360. * @param {Function} cb callback function
  361. * @return {Void}
  362. */
  363. Application.afterStart = function(cb) {
  364. if(this.state !== STATE_START) {
  365. utils.invokeCallback(cb, new Error('application is not running now.'));
  366. return;
  367. }
  368. var afterFun = this.lifecycleCbs[Constants.LIFECYCLE.AFTER_STARTUP];
  369. var self = this;
  370. appUtil.optComponents(this.loaded, Constants.RESERVED.AFTER_START, function(err) {
  371. self.state = STATE_STARTED;
  372. var id = self.getServerId();
  373. if(!err) {
  374. logger.info('%j finish start', id);
  375. }
  376. if(!!afterFun) {
  377. afterFun.call(null, self, function() {
  378. utils.invokeCallback(cb, err);
  379. });
  380. } else {
  381. utils.invokeCallback(cb, err);
  382. }
  383. var usedTime = Date.now() - self.startTime;
  384. logger.info('%j startup in %s ms', id, usedTime);
  385. self.event.emit(events.START_SERVER, id);
  386. });
  387. };
  388. /**
  389. * Stop components.
  390. *
  391. * @param {Boolean} force whether stop the app immediately
  392. */
  393. Application.stop = function(force) {
  394. if(this.state > STATE_STARTED) {
  395. logger.warn('[pomelo application] application is not running now.');
  396. return;
  397. }
  398. this.state = STATE_STOPED;
  399. var self = this;
  400. this.stopTimer = setTimeout(function() {
  401. process.exit(0);
  402. }, Constants.TIME.TIME_WAIT_STOP);
  403. var cancelShutDownTimer =function(){
  404. if(!!self.stopTimer) {
  405. clearTimeout(self.stopTimer);
  406. }
  407. };
  408. var shutDown = function() {
  409. appUtil.stopComps(self.loaded, 0, force, function() {
  410. cancelShutDownTimer();
  411. if(force) {
  412. process.exit(0);
  413. }
  414. });
  415. };
  416. var fun = this.get(Constants.KEYWORDS.BEFORE_STOP_HOOK);
  417. var stopFun = this.lifecycleCbs[Constants.LIFECYCLE.BEFORE_SHUTDOWN];
  418. if(!!stopFun) {
  419. stopFun.call(null, this, shutDown, cancelShutDownTimer);
  420. } else if(!!fun) {
  421. utils.invokeCallback(fun, self, shutDown, cancelShutDownTimer);
  422. } else {
  423. shutDown();
  424. }
  425. };
  426. /**
  427. * Assign `setting` to `val`, or return `setting`'s value.
  428. *
  429. * Example:
  430. *
  431. * app.set('key1', 'value1');
  432. * app.get('key1'); // 'value1'
  433. * app.key1; // undefined
  434. *
  435. * app.set('key2', 'value2', true);
  436. * app.get('key2'); // 'value2'
  437. * app.key2; // 'value2'
  438. *
  439. * @param {String} setting the setting of application
  440. * @param {String} val the setting's value
  441. * @param {Boolean} attach whether attach the settings to application
  442. * @return {Server|Mixed} for chaining, or the setting value
  443. * @memberOf Application
  444. */
  445. Application.set = function (setting, val, attach) {
  446. if (arguments.length === 1) {
  447. return this.settings[setting];
  448. }
  449. this.settings[setting] = val;
  450. if(attach) {
  451. this[setting] = val;
  452. }
  453. return this;
  454. };
  455. /**
  456. * Get property from setting
  457. *
  458. * @param {String} setting application setting
  459. * @return {String} val
  460. * @memberOf Application
  461. */
  462. Application.get = function (setting) {
  463. return this.settings[setting];
  464. };
  465. /**
  466. * Check if `setting` is enabled.
  467. *
  468. * @param {String} setting application setting
  469. * @return {Boolean}
  470. * @memberOf Application
  471. */
  472. Application.enabled = function (setting) {
  473. return !!this.get(setting);
  474. };
  475. /**
  476. * Check if `setting` is disabled.
  477. *
  478. * @param {String} setting application setting
  479. * @return {Boolean}
  480. * @memberOf Application
  481. */
  482. Application.disabled = function (setting) {
  483. return !this.get(setting);
  484. };
  485. /**
  486. * Enable `setting`.
  487. *
  488. * @param {String} setting application setting
  489. * @return {app} for chaining
  490. * @memberOf Application
  491. */
  492. Application.enable = function (setting) {
  493. return this.set(setting, true);
  494. };
  495. /**
  496. * Disable `setting`.
  497. *
  498. * @param {String} setting application setting
  499. * @return {app} for chaining
  500. * @memberOf Application
  501. */
  502. Application.disable = function (setting) {
  503. return this.set(setting, false);
  504. };
  505. /**
  506. * Configure callback for the specified env and server type.
  507. * When no env is specified that callback will
  508. * be invoked for all environments and when no type is specified
  509. * that callback will be invoked for all server types.
  510. *
  511. * Examples:
  512. *
  513. * app.configure(function(){
  514. * // executed for all envs and server types
  515. * });
  516. *
  517. * app.configure('development', function(){
  518. * // executed development env
  519. * });
  520. *
  521. * app.configure('development', 'connector', function(){
  522. * // executed for development env and connector server type
  523. * });
  524. *
  525. * @param {String} env application environment
  526. * @param {Function} fn callback function
  527. * @param {String} type server type
  528. * @return {Application} for chaining
  529. * @memberOf Application
  530. */
  531. Application.configure = function (env, type, fn) {
  532. var args = [].slice.call(arguments);
  533. fn = args.pop();
  534. env = type = Constants.RESERVED.ALL;
  535. if(args.length > 0) {
  536. env = args[0];
  537. }
  538. if(args.length > 1) {
  539. type = args[1];
  540. }
  541. if (env === Constants.RESERVED.ALL || contains(this.settings.env, env)) {
  542. if (type === Constants.RESERVED.ALL || contains(this.settings.serverType, type)) {
  543. fn.call(this);
  544. }
  545. }
  546. return this;
  547. };
  548. /**
  549. * Register admin modules. Admin modules is the extends point of the monitor system.
  550. *
  551. * @param {String} module (optional) module id or provoided by module.moduleId
  552. * @param {Object} module module object or factory function for module
  553. * @param {Object} opts construct parameter for module
  554. * @memberOf Application
  555. */
  556. Application.registerAdmin = function(moduleId, module, opts) {
  557. var modules = this.get(Constants.KEYWORDS.MODULE);
  558. if(!modules) {
  559. modules = {};
  560. this.set(Constants.KEYWORDS.MODULE, modules);
  561. }
  562. if(typeof moduleId !== 'string') {
  563. opts = module;
  564. module = moduleId;
  565. if(module) {
  566. moduleId = module.moduleId;
  567. }
  568. }
  569. if(!moduleId){
  570. return;
  571. }
  572. modules[moduleId] = {
  573. moduleId: moduleId,
  574. module: module,
  575. opts: opts
  576. };
  577. };
  578. /**
  579. * Use plugin.
  580. *
  581. * @param {Object} plugin plugin instance
  582. * @param {[type]} opts (optional) construct parameters for the factory function
  583. * @memberOf Application
  584. */
  585. Application.use = function(plugin, opts) {
  586. if(!plugin.components) {
  587. logger.error('invalid components, no components exist');
  588. return;
  589. }
  590. var self = this;
  591. opts = opts || {};
  592. var dir = path.dirname(plugin.components);
  593. if(!fs.existsSync(plugin.components)) {
  594. logger.error('fail to find components, find path: %s', plugin.components);
  595. return;
  596. }
  597. fs.readdirSync(plugin.components).forEach(function (filename) {
  598. if (!/\.js$/.test(filename)) {
  599. return;
  600. }
  601. var name = path.basename(filename, '.js');
  602. var param = opts[name] || {};
  603. var absolutePath = path.join(dir, Constants.DIR.COMPONENT, filename);
  604. if(!fs.existsSync(absolutePath)) {
  605. logger.error('component %s not exist at %s', name, absolutePath);
  606. } else {
  607. self.load(require(absolutePath), param);
  608. }
  609. });
  610. // load events
  611. if(!plugin.events) {
  612. return;
  613. } else {
  614. if(!fs.existsSync(plugin.events)) {
  615. logger.error('fail to find events, find path: %s', plugin.events);
  616. return;
  617. }
  618. fs.readdirSync(plugin.events).forEach(function (filename) {
  619. if (!/\.js$/.test(filename)) {
  620. return;
  621. }
  622. var absolutePath = path.join(dir, Constants.DIR.EVENT, filename);
  623. if(!fs.existsSync(absolutePath)) {
  624. logger.error('events %s not exist at %s', filename, absolutePath);
  625. } else {
  626. bindEvents(require(absolutePath), self);
  627. }
  628. });
  629. }
  630. };
  631. /**
  632. * Application transaction. Transcation includes conditions and handlers, if conditions are satisfied, handlers would be executed.
  633. * And you can set retry times to execute handlers. The transaction log is in file logs/transaction.log.
  634. *
  635. * @param {String} name transaction name
  636. * @param {Object} conditions functions which are called before transaction
  637. * @param {Object} handlers functions which are called during transaction
  638. * @param {Number} retry retry times to execute handlers if conditions are successfully executed
  639. * @memberOf Application
  640. */
  641. Application.transaction = function(name, conditions, handlers, retry) {
  642. appManager.transaction(name, conditions, handlers, retry);
  643. };
  644. /**
  645. * Get master server info.
  646. *
  647. * @return {Object} master server info, {id, host, port}
  648. * @memberOf Application
  649. */
  650. Application.getMaster = function() {
  651. return this.master;
  652. };
  653. /**
  654. * Get current server info.
  655. *
  656. * @return {Object} current server info, {id, serverType, host, port}
  657. * @memberOf Application
  658. */
  659. Application.getCurServer = function() {
  660. return this.curServer;
  661. };
  662. /**
  663. * Get current server id.
  664. *
  665. * @return {String|Number} current server id from servers.json
  666. * @memberOf Application
  667. */
  668. Application.getServerId = function() {
  669. return this.serverId;
  670. };
  671. /**
  672. * Get current server type.
  673. *
  674. * @return {String|Number} current server type from servers.json
  675. * @memberOf Application
  676. */
  677. Application.getServerType = function() {
  678. return this.serverType;
  679. };
  680. /**
  681. * Get all the current server infos.
  682. *
  683. * @return {Object} server info map, key: server id, value: server info
  684. * @memberOf Application
  685. */
  686. Application.getServers = function() {
  687. return this.servers;
  688. };
  689. /**
  690. * Get all server infos from servers.json.
  691. *
  692. * @return {Object} server info map, key: server id, value: server info
  693. * @memberOf Application
  694. */
  695. Application.getServersFromConfig = function() {
  696. return this.get(Constants.KEYWORDS.SERVER_MAP);
  697. };
  698. /**
  699. * Get all the server type.
  700. *
  701. * @return {Array} server type list
  702. * @memberOf Application
  703. */
  704. Application.getServerTypes = function() {
  705. return this.serverTypes;
  706. };
  707. /**
  708. * Get server info by server id from current server cluster.
  709. *
  710. * @param {String} serverId server id
  711. * @return {Object} server info or undefined
  712. * @memberOf Application
  713. */
  714. Application.getServerById = function(serverId) {
  715. return this.servers[serverId];
  716. };
  717. /**
  718. * Get server info by server id from servers.json.
  719. *
  720. * @param {String} serverId server id
  721. * @return {Object} server info or undefined
  722. * @memberOf Application
  723. */
  724. Application.getServerFromConfig = function(serverId) {
  725. return this.get(Constants.KEYWORDS.SERVER_MAP)[serverId];
  726. };
  727. /**
  728. * Get server infos by server type.
  729. *
  730. * @param {String} serverType server type
  731. * @return {Array} server info list
  732. * @memberOf Application
  733. */
  734. Application.getServersByType = function(serverType) {
  735. return this.serverTypeMaps[serverType];
  736. };
  737. /**
  738. * Check the server whether is a frontend server
  739. *
  740. * @param {server} server server info. it would check current server
  741. * if server not specified
  742. * @return {Boolean}
  743. *
  744. * @memberOf Application
  745. */
  746. Application.isFrontend = function(server) {
  747. server = server || this.getCurServer();
  748. return !!server && server.frontend === 'true';
  749. };
  750. /**
  751. * Check the server whether is a backend server
  752. *
  753. * @param {server} server server info. it would check current server
  754. * if server not specified
  755. * @return {Boolean}
  756. * @memberOf Application
  757. */
  758. Application.isBackend = function(server) {
  759. server = server || this.getCurServer();
  760. return !!server && !server.frontend;
  761. };
  762. /**
  763. * Check whether current server is a master server
  764. *
  765. * @return {Boolean}
  766. * @memberOf Application
  767. */
  768. Application.isMaster = function() {
  769. return this.serverType === Constants.RESERVED.MASTER;
  770. };
  771. /**
  772. * Add new server info to current application in runtime.
  773. *
  774. * @param {Array} servers new server info list
  775. * @memberOf Application
  776. */
  777. Application.addServers = function(servers) {
  778. if(!servers || !servers.length) {
  779. return;
  780. }
  781. var item, slist;
  782. for(var i=0, l=servers.length; i<l; i++) {
  783. item = servers[i];
  784. // update global server map
  785. this.servers[item.id] = item;
  786. // update global server type map
  787. slist = this.serverTypeMaps[item.serverType];
  788. if(!slist) {
  789. this.serverTypeMaps[item.serverType] = slist = [];
  790. }
  791. replaceServer(slist, item);
  792. // update global server type list
  793. if(this.serverTypes.indexOf(item.serverType) < 0) {
  794. this.serverTypes.push(item.serverType);
  795. }
  796. }
  797. this.event.emit(events.ADD_SERVERS, servers);
  798. };
  799. /**
  800. * Remove server info from current application at runtime.
  801. *
  802. * @param {Array} ids server id list
  803. * @memberOf Application
  804. */
  805. Application.removeServers = function(ids) {
  806. if(!ids || !ids.length) {
  807. return;
  808. }
  809. var id, item, slist;
  810. for(var i=0, l=ids.length; i<l; i++) {
  811. id = ids[i];
  812. item = this.servers[id];
  813. if(!item) {
  814. continue;
  815. }
  816. // clean global server map
  817. delete this.servers[id];
  818. // clean global server type map
  819. slist = this.serverTypeMaps[item.serverType];
  820. removeServer(slist, id);
  821. // TODO: should remove the server type if the slist is empty?
  822. }
  823. this.event.emit(events.REMOVE_SERVERS, ids);
  824. };
  825. /**
  826. * Replace server info from current application at runtime.
  827. *
  828. * @param {Object} server id map
  829. * @memberOf Application
  830. */
  831. Application.replaceServers = function(servers) {
  832. if(!servers){
  833. return;
  834. }
  835. this.servers = servers;
  836. this.serverTypeMaps = {};
  837. this.serverTypes = [];
  838. var serverArray = [];
  839. for(var id in servers){
  840. var server = servers[id];
  841. var serverType = server[Constants.RESERVED.SERVER_TYPE];
  842. var slist = this.serverTypeMaps[serverType];
  843. if(!slist) {
  844. this.serverTypeMaps[serverType] = slist = [];
  845. }
  846. this.serverTypeMaps[serverType].push(server);
  847. // update global server type list
  848. if(this.serverTypes.indexOf(serverType) < 0) {
  849. this.serverTypes.push(serverType);
  850. }
  851. serverArray.push(server);
  852. }
  853. this.event.emit(events.REPLACE_SERVERS, serverArray);
  854. };
  855. /**
  856. * Add crons from current application at runtime.
  857. *
  858. * @param {Array} crons new crons would be added in application
  859. * @memberOf Application
  860. */
  861. Application.addCrons = function(crons) {
  862. if(!crons || !crons.length) {
  863. logger.warn('crons is not defined.');
  864. return;
  865. }
  866. this.event.emit(events.ADD_CRONS, crons);
  867. };
  868. /**
  869. * Remove crons from current application at runtime.
  870. *
  871. * @param {Array} crons old crons would be removed in application
  872. * @memberOf Application
  873. */
  874. Application.removeCrons = function(crons) {
  875. if(!crons || !crons.length) {
  876. logger.warn('ids is not defined.');
  877. return;
  878. }
  879. this.event.emit(events.REMOVE_CRONS, crons);
  880. };
  881. var replaceServer = function(slist, serverInfo) {
  882. for(var i=0, l=slist.length; i<l; i++) {
  883. if(slist[i].id === serverInfo.id) {
  884. slist[i] = serverInfo;
  885. return;
  886. }
  887. }
  888. slist.push(serverInfo);
  889. };
  890. var removeServer = function(slist, id) {
  891. if(!slist || !slist.length) {
  892. return;
  893. }
  894. for(var i=0, l=slist.length; i<l; i++) {
  895. if(slist[i].id === id) {
  896. slist.splice(i, 1);
  897. return;
  898. }
  899. }
  900. };
  901. var contains = function(str, settings) {
  902. if(!settings) {
  903. return false;
  904. }
  905. var ts = settings.split("|");
  906. for(var i=0, l=ts.length; i<l; i++) {
  907. if(str === ts[i]) {
  908. return true;
  909. }
  910. }
  911. return false;
  912. };
  913. var bindEvents = function(Event, app) {
  914. var emethods = new Event(app);
  915. for(var m in emethods) {
  916. if(typeof emethods[m] === 'function') {
  917. app.event.on(m, emethods[m].bind(emethods));
  918. }
  919. }
  920. };
  921. var addFilter = function(app, type, filter) {
  922. var filters = app.get(type);
  923. if(!filters) {
  924. filters = [];
  925. app.set(type, filters);
  926. }
  927. filters.push(filter);
  928. };