UNPKG

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