1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | function Kernel (config, boot, run, shutdown, loglevel) {
|
32 | |
33 |
|
34 |
|
35 | if (loglevel !== this.LOGLEVEL_DEBUG &&
|
36 | loglevel !== this.LOGLEVEL_NORMAL &&
|
37 | loglevel !== this.LOGLEVEL_QUIET) {
|
38 | loglevel = this.LOGLEVEL_NORMAL;
|
39 | }
|
40 | this.loglevel = loglevel;
|
41 | |
42 |
|
43 |
|
44 | this.appdir = process.cwd() + '/';
|
45 | |
46 |
|
47 |
|
48 | this.utility = this.include(this.kerneldir + 'utility').singleton();
|
49 | |
50 |
|
51 |
|
52 | this.config = this.include(this.kerneldir + 'config').singleton();
|
53 | if (config) {
|
54 | for (var key in config) {
|
55 | this.config[key] = config[key];
|
56 | }
|
57 | }
|
58 | this.debug(
|
59 | 'merged config : \n' + util.inspect(this.config, { depth : 0 })
|
60 | , this._debugfile
|
61 | );
|
62 | |
63 |
|
64 |
|
65 | if (this.loglevel !== this.LOGLEVEL_QUIET) {
|
66 | console.log();
|
67 | console.log(this.utility.ansi('yellow', this.config.project));
|
68 | console.log();
|
69 | }
|
70 | |
71 |
|
72 |
|
73 | this._fetchlinks();
|
74 | |
75 |
|
76 |
|
77 | this._listen();
|
78 | |
79 |
|
80 |
|
81 | this.log('Lemonade ' + this.version + ' (' + this.name + ')');
|
82 | this.log('app path : ' + this.appdir);
|
83 | |
84 |
|
85 |
|
86 | if (!fs.existsSync(this.config.logpath)) {
|
87 | try {
|
88 | fs.mkdirSync(this.config.logpath);
|
89 | this.log(
|
90 | 'creating log path : ' + this.config.logpath + ' - ' +
|
91 | this.utility.ansi('green', 'ok')
|
92 | );
|
93 | } catch (e) {
|
94 | this.error(
|
95 | new Error('Failed to create log path : ' + e)
|
96 | );
|
97 | }
|
98 | } else {
|
99 | this.log('log path : ' + this.config.logpath);
|
100 | }
|
101 | |
102 |
|
103 |
|
104 | var links = { boot : boot, run : run, shutdown : shutdown };
|
105 | this.debug(
|
106 | 'preparing to attach links : \n' + util.inspect(links)
|
107 | , this._debugfile
|
108 | );
|
109 | for (var level in links) {
|
110 | for (var link in links[level]) {
|
111 | this._attach(
|
112 | link, links[level][link], level
|
113 | );
|
114 | }
|
115 | }
|
116 | this.debug(
|
117 | 'kernellinks : \n' + util.inspect(this._kernellinks, { depth : 3 })
|
118 | , this._debugfile
|
119 | );
|
120 | |
121 |
|
122 |
|
123 | this._heartbeat();
|
124 | }
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | var
|
130 | events = require('events')
|
131 | , util = require('util')
|
132 | , fs = require('fs')
|
133 | , os = require('os')
|
134 | , include = require('./include.js');
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | util.inherits(Kernel, events.EventEmitter);
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | Kernel.prototype.version = require('../package.json').version;
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 | Kernel.prototype.name = 'Chrysanths';
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | Kernel.prototype.HEARTBEAT = 'hearbeat';
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | Kernel.prototype.ONLINE = 'kernel-online';
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | Kernel.prototype.OFFLINE = 'kernel-offline';
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 | Kernel.prototype.LOG = 'log';
|
180 | Kernel.prototype.ERROR = 'error';
|
181 | Kernel.prototype.FATAL = 'fatal-error';
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 | Kernel.prototype._LINK_READY = 'link-ready';
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 | Kernel.prototype._RUNLEVEL_0 = 0;
|
195 | Kernel.prototype._RUNLEVEL_BOOT = 'boot';
|
196 | Kernel.prototype._RUNLEVEL_RUN = 'run';
|
197 | Kernel.prototype._RUNLEVEL_SHUTDOWN = 'shutdown';
|
198 | Kernel.prototype._currentrunlevel = 0;
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | Kernel.prototype.kerneldir = __dirname + '/';
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | Kernel.prototype.appdir = '';
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 | Kernel.prototype.config = {};
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | Kernel.prototype.utility = {};
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 | Kernel.prototype.LOGLEVEL_DEBUG = 'loglevel-debug';
|
229 | Kernel.prototype.LOGLEVEL_NORMAL = 'loglevel-normal';
|
230 | Kernel.prototype.LOGLEVEL_QUIET = 'loglevel-quiet';
|
231 | Kernel.prototype.loglevel = '';
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | Kernel.prototype._debugfile = 'kernel.debug';
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 | Kernel.prototype._availablelinks = {};
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 | Kernel.prototype._kernellinks = {
|
251 | |
252 |
|
253 |
|
254 |
|
255 |
|
256 | 0 : { }
|
257 | , boot : { }
|
258 | , run : { }
|
259 | , shutdown : { }
|
260 | };
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 | Kernel.prototype._queuedfornow = {};
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 | Kernel.prototype._heartbeatintv;
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 | Kernel.prototype.include = function(path) {
|
285 | return include(path, this, this);
|
286 | };
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 | Kernel.prototype.status = function() {
|
293 |
|
294 | var memory = process.memoryUsage()
|
295 | ,status = {};
|
296 |
|
297 | status.kernel = {
|
298 |
|
299 | hostname : os.hostname
|
300 | ,memory : {
|
301 | heapUsed : memory.heapUsed
|
302 | ,heapTotal : memory.heapTotal
|
303 | }
|
304 | ,load : os.loadavg()
|
305 | ,node : process.version
|
306 | ,lemonade : {
|
307 | version : this.version
|
308 | ,name : this.name
|
309 | }
|
310 |
|
311 | };
|
312 |
|
313 | for (var level in this._kernellinks) {
|
314 | for (var link in this._kernellinks[level]) {
|
315 | var linkstatus = this._kernellinks[level][link].obj.status();
|
316 | if (linkstatus) {
|
317 | status[link] = linkstatus;
|
318 | } else {
|
319 | status[link] = '';
|
320 | }
|
321 | }
|
322 | }
|
323 |
|
324 | return status;
|
325 | };
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 | Kernel.prototype.shutdown = function(message) {
|
332 | this._runlevel(this._RUNLEVEL_SHUTDOWN, message);
|
333 | };
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 | Kernel.prototype.log = function(message, file, date) {
|
342 | |
343 |
|
344 |
|
345 | if (message instanceof Error) {
|
346 | msg = this.utility.ansi('red', message.message);
|
347 | msg += '\n\n\n' + message.stack + '\n\n';
|
348 | message = msg;
|
349 | } else if (this.loglevel === this.LOGLEVEL_QUIET) {
|
350 | return;
|
351 | }
|
352 | if (typeof file !== 'undefined') {
|
353 | var date = this.utility.date('yyyy-mm-dd HH:ii:ss', date)
|
354 | ,logfile = this.config.logpath + file + '.log';
|
355 | fs.appendFile(
|
356 | logfile
|
357 | ,date + ' ' + message + '\n'
|
358 | ,function() {}
|
359 | );
|
360 | } else {
|
361 | util.log(message);
|
362 | }
|
363 | };
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
369 | Kernel.prototype.error = function(error) {
|
370 | if (!(error instanceof Error)) {
|
371 | error = new Error('Unprefixed error : ' + error);
|
372 | }
|
373 | this.log(error);
|
374 | };
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 | Kernel.prototype.fatal = function(message) {
|
381 | this.error(message);
|
382 | this.shutdown('fatal error');
|
383 | };
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 | Kernel.prototype.debug = function(message, logfile) {
|
392 | if (this.loglevel === this.LOGLEVEL_DEBUG) {
|
393 | if (typeof message === 'function') {
|
394 | message();
|
395 | } else {
|
396 | this.log('DEBUG : ' + message, logfile);
|
397 | }
|
398 | }
|
399 | };
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 | Kernel.prototype._fetchlinks = function() {
|
406 | var fetch = function(dir) {
|
407 | var arr = fs.readdirSync(dir);
|
408 | for (var i = 0, max = arr.length; i < max; i++) {
|
409 | var fname = arr[i].substring(0, arr[i].length-3);
|
410 | this._availablelinks[fname] = dir + '/' + fname;
|
411 | }
|
412 | }.bind(this);
|
413 | fetch(this.kerneldir + this.config._KERNEL_DIR_LINKS);
|
414 | this.debug(
|
415 | 'base links : \n' + util.inspect(this._availablelinks)
|
416 | , this._debugfile
|
417 | );
|
418 | if (fs.existsSync(this.appdir + this.config._KERNEL_DIR_LINKS)) {
|
419 | fetch(this.appdir + this.config._KERNEL_DIR_LINKS);
|
420 | this.debug(
|
421 | 'found custom links dir, read it and merged : \n'
|
422 | + util.inspect(this._availablelinks)
|
423 | , this._debugfile
|
424 | );
|
425 | }
|
426 | };
|
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 | Kernel.prototype._runlevel = function(level, message) {
|
436 | |
437 |
|
438 |
|
439 | if (!level || (level === this._currentrunlevel)) {
|
440 | return this._currentrunlevel;
|
441 | }
|
442 | |
443 |
|
444 |
|
445 | this._currentrunlevel = level;
|
446 | this.log(this.utility.ansi('blue',
|
447 | '[ '
|
448 | + this._currentrunlevel
|
449 | + ((message) ? ' - ' + message : '')
|
450 | +
|
451 | ' ]'
|
452 | ));
|
453 | |
454 |
|
455 |
|
456 | var trigger = function(action, linkloader, linkname, level) {
|
457 | process.nextTick(function() {
|
458 | this._queuedfornow[linkname] = true;
|
459 | if (typeof linkloader.obj[action] === 'function') {
|
460 | try {
|
461 | if (action === 'load') {
|
462 | this.debug(
|
463 | linkname + '.' + action + '(' + util.inspect(linkloader.loadparams) + ')'
|
464 | );
|
465 | |
466 |
|
467 |
|
468 |
|
469 | linkloader.loadparams.push(function(err, msg) {
|
470 | if (err) {
|
471 | this.fatal(
|
472 | new Error('[ ' + level + ' ][1] Link error - ' + linkname + ' : ' + err)
|
473 | );
|
474 | } else {
|
475 | |
476 |
|
477 |
|
478 | delete this._queuedfornow[linkname];
|
479 | this.emit(this._LINK_READY, linkname, msg);
|
480 | }
|
481 | }.bind(this));
|
482 | linkloader.obj[action].apply(linkloader.obj, linkloader.loadparams);
|
483 | } else {
|
484 | this.debug(
|
485 | linkname + '.' + action + '()'
|
486 | );
|
487 | linkloader.obj[action](function(err, msg) {
|
488 | if (err) {
|
489 | this.fatal(
|
490 | new Error('[ ' + level + ' ][3] Link error - ' + linkname + ' : ' + err)
|
491 | );
|
492 | } else {
|
493 | delete this._queuedfornow[linkname];
|
494 | this.emit(this._LINK_READY, linkname, msg);
|
495 | }
|
496 | }.bind(this));
|
497 | }
|
498 | } catch (e) {
|
499 | this.fatal(
|
500 | new Error('[ ' + level + ' ][2] Link error - ' + linkname + ' : ' + e)
|
501 | );
|
502 | }
|
503 | } else {
|
504 | |
505 |
|
506 |
|
507 | delete this._queuedfornow[linkname];
|
508 | this.emit(this._LINK_READY, linkname, 'skipping');
|
509 | }
|
510 | }.bind(this));
|
511 | }.bind(this);
|
512 | |
513 |
|
514 |
|
515 | if (this._currentrunlevel === this._RUNLEVEL_SHUTDOWN) {
|
516 | for (var level in this._kernellinks) {
|
517 | if (level !== this._RUNLEVEL_SHUTDOWN) {
|
518 | var currentlinks = this._kernellinks[level];
|
519 | for (var link in currentlinks) {
|
520 | trigger(
|
521 | 'unload'
|
522 | ,currentlinks[link]
|
523 | ,link
|
524 | ,level
|
525 | );
|
526 | }
|
527 | }
|
528 | }
|
529 | } else {
|
530 | |
531 |
|
532 |
|
533 | var currentlinks = this._kernellinks[this._currentrunlevel];
|
534 | for (var link in currentlinks) {
|
535 | trigger(
|
536 | 'load'
|
537 | ,currentlinks[link]
|
538 | ,link
|
539 | ,this._currentrunlevel
|
540 | );
|
541 | }
|
542 | }
|
543 | };
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 | Kernel.prototype._heartbeat = function () {
|
550 | |
551 |
|
552 |
|
553 | if (!this._heartbeatintv) {
|
554 | this._heartbeatintv = setInterval(function() {
|
555 | this._heartbeat();
|
556 | }.bind(this), 1000);
|
557 | }
|
558 | |
559 |
|
560 |
|
561 |
|
562 |
|
563 | var left = Object.keys(this._queuedfornow);
|
564 | if (left.length > 0) {
|
565 | |
566 |
|
567 |
|
568 | this.log(
|
569 | 'zZz waiting for links (' + this.utility.ansi('red', left.join(',')) + ')'
|
570 | );
|
571 | } else {
|
572 | |
573 |
|
574 |
|
575 | switch (this._currentrunlevel) {
|
576 | case this._RUNLEVEL_0 :
|
577 | |
578 |
|
579 |
|
580 | this._runlevel(this._RUNLEVEL_BOOT);
|
581 | break;
|
582 | case this._RUNLEVEL_BOOT :
|
583 | |
584 |
|
585 |
|
586 | this._runlevel(this._RUNLEVEL_RUN);
|
587 | break;
|
588 | case this._RUNLEVEL_RUN :
|
589 | |
590 |
|
591 |
|
592 | this.emit(this.ONLINE);
|
593 | break;
|
594 | case this._RUNLEVEL_SHUTDOWN :
|
595 | |
596 |
|
597 |
|
598 | this.emit(this.OFFLINE);
|
599 | clearInterval(this._heartbeatintv);
|
600 | break;
|
601 | }
|
602 | }
|
603 | |
604 |
|
605 |
|
606 | this.emit(this.HEARTBEAT);
|
607 | };
|
608 |
|
609 |
|
610 |
|
611 |
|
612 |
|
613 | Kernel.prototype._listen = function() {
|
614 | |
615 |
|
616 |
|
617 | this.on(this.LOG, function(message) {
|
618 | this.log(message);
|
619 | }.bind(this));
|
620 | |
621 |
|
622 |
|
623 | this.on(this.ERROR, function(message) {
|
624 | this.error(message);
|
625 | }.bind(this));
|
626 | |
627 |
|
628 |
|
629 | this.on(this.FATAL, function(message) {
|
630 | this.fatal(message);
|
631 | }.bind(this));
|
632 | |
633 |
|
634 |
|
635 | this.on(this._LINK_READY, function(link, msg) {
|
636 | this.log(
|
637 | this.utility.ansi('green', 'ok') + ' - '
|
638 | + link + (msg ? ' ' + msg : '')
|
639 | );
|
640 | }.bind(this));
|
641 | };
|
642 |
|
643 |
|
644 |
|
645 |
|
646 |
|
647 |
|
648 |
|
649 |
|
650 | Kernel.prototype._attach = function(key, value, level) {
|
651 | var linkloader = {}, module;
|
652 | |
653 |
|
654 |
|
655 | if (typeof value === 'function') {
|
656 | this.debug(
|
657 | 'attaching function ' + key + ' : \n' + value
|
658 | , this._debugfile
|
659 | );
|
660 | linkloader = value;
|
661 | }
|
662 | |
663 |
|
664 |
|
665 | else if (this._availablelinks[value] ||
|
666 | (value instanceof Array && this._availablelinks[value[0]])) {
|
667 |
|
668 | module = this.include(
|
669 | this._availablelinks[((value instanceof Array) ? value[0] : value)]
|
670 | );
|
671 | this.debug(
|
672 | 'attaching availablelink ' + key +': \n' + util.inspect(module, { depth : 0 })
|
673 | , this._debugfile
|
674 | );
|
675 | if (module && typeof module.new === 'function') {
|
676 | linkloader = module.new();
|
677 | }
|
678 | }
|
679 | |
680 |
|
681 |
|
682 |
|
683 | else if (fs.existsSync(this.appdir + value + '.js')) {
|
684 | try {
|
685 | module = this.include(this.appdir + value);
|
686 | this.debug(
|
687 | 'attaching included module ' + key +': \n' + util.inspect(module, { depth : 0 })
|
688 | , this._debugfile
|
689 | );
|
690 | if (module && typeof module.new === 'function') {
|
691 | linkloader = module.new();
|
692 | }
|
693 | } catch (e) {
|
694 | this.error(
|
695 | new Error('Couldn\'t load ' + key + ' from ' + this.appdir + value + ' : ' + e)
|
696 | );
|
697 | }
|
698 | }
|
699 | |
700 |
|
701 |
|
702 | if (typeof linkloader.link === 'function') {
|
703 | var params = [];
|
704 | if (value instanceof Array) {
|
705 | for (var i = 1, max = value.length; i < max; i++) {
|
706 | params.push(value[i]);
|
707 | }
|
708 | }
|
709 | |
710 |
|
711 |
|
712 | this._kernellinks[level][key] = {
|
713 | obj : linkloader, loadparams : params, ready : {}
|
714 | };
|
715 | |
716 |
|
717 |
|
718 | linkloader.label = key;
|
719 | this[key] = linkloader.link.bind(linkloader);
|
720 | } else {
|
721 | |
722 |
|
723 |
|
724 | this.fatal(
|
725 | new Error('Invalid link : ' + key)
|
726 | );
|
727 | }
|
728 | };
|
729 |
|
730 |
|
731 |
|
732 |
|
733 | exports = module.exports = Kernel; |
\ | No newline at end of file |