'use strict' ; import { ApplicationHandler } from '../application/ApplicationHandler' ; import { DAEMON_MESSENGER , SEND_DAEMON_MESSAGE , State } from '../const' ; import * as fs from 'fs' ; import assert = require ( 'assert' ) ; import messenger from 'pandora-messenger' ; import { getDaemonLogger } from '../universal/LoggerBroker' ; import { ApplicationRepresentation , Monitor } from '../domain' ; import { DaemonIntrospection } from './DaemonIntrospection' ; import { GlobalConfigProcessor } from '../universal/GlobalConfigProcessor' ; /**
 * Class Daemon
 */ export class Daemon { private messengerServer : any ; private monitor : Monitor ; private introspection : DaemonIntrospection ; private daemonLogger = getDaemonLogger ( ) ; private globalConfigProcesser = GlobalConfigProcessor . getInstance ( ) ; private globalConfig = this . globalConfigProcesser . getAllProperties ( ) ; public state : State ; public apps : Map < any , ApplicationHandler > ; constructor ( ) { this . state = State . pending ; this . apps = new Map ( ) ; } return { Promise < any > } async start ( ) { if ( this . state === State . complete ) { return ; } return new Promise ( ( resolve , reject ) => { this . messengerServer = messenger . getServer ( { name : DAEMON_MESSENGER , } ) ; this . messengerServer . on ( SEND_DAEMON_MESSAGE , ( message , reply ) => { this . handleCommand ( message , reply ) ; } ) ; this . messengerServer . ready ( ( ) => { return this . startMonitor ( ) . then ( ( ) => { this . state = State . complete ; resolve ( ) ; } ) . catch ( err => { this . daemonLogger . error ( err ) ; process . exit ( 1 ) ; reject ( err ) ; } ) ; } ) ; this . handleExit ( ) ; } ) ; } /**
     * Handing all passive exit events, such as process signal, uncaughtException
     */ handleExit ( ) { process . on ( 'uncaughtException' , ( err ) => { this . daemonLogger . error ( err ) ; this . stop ( ) ; } ) ; [ 'SIGQUIT' , 'SIGTERM' , 'SIGINT' ] . forEach ( ( sig ) => { process . once ( sig , ( ) => { this . stop ( ) ; } ) ; } ) ; process . once ( 'exit' , ( ) => { this . stop ( ) ; } ) ; } param { ApplicationRepresentation } applicationRepresentation returns { Promise < ApplicationHandler > } async startApp ( applicationRepresentation ) { const appName = applicationRepresentation . appName ; const appDir = applicationRepresentation . appDir ; assert ( appName , `options.appName is required!` ) ; assert ( appDir , `options.appDir is required!` ) ; assert ( fs . existsSync ( appDir ) , `${ appDir } does not exists!` ) ; assert ( ! this . apps . has ( appName ) , `app[${ appName }] has been initialized!` ) ; const applicationHandler = new ApplicationHandler ( applicationRepresentation ) ; await applicationHandler . start ( ) ; this . apps . set ( appName , applicationHandler ) ; return applicationHandler ; } param appName param processName return { Promise < ApplicationHandler > } async reloadApp ( appName , processName ? ) { const handler = this . apps . get ( appName ) ; if ( ! handler ) { throw new Error ( `${ appName } does not exists!` ) ; } await handler . reload ( processName ) ; return handler ; } param appName return { Promise < ApplicationHandler > } async stopApp ( appName ) { const handler = this . apps . get ( appName ) ; if ( ! handler ) { throw new Error ( `${ appName } does not exists!` ) ; } await handler . stop ( ) ; this . apps . delete ( appName ) ; return handler ; } return { Promise < void > } async stopAllApps ( ) { for ( const appName of this . apps . keys ( ) ) { await this . stopApp ( appName ) ; } } return { Promise < void > } async stop ( ) { this . state = State . stopped ; await this . stopAllApps ( ) ; this . daemonLogger . info ( 'daemon is going to stop' ) ; this . messengerServer . close ( ) ; await this . stopMonitor ( ) ; this . state = State . stopped ; } return { Promise < void > } private async startMonitor ( ) { if ( ! this . monitor ) { this . monitor = new this . globalConfig [ 'monitor' ] ( ) ; } return this . monitor . start ( ) ; } return { Promise < void > } private async stopMonitor ( ) { if ( this . monitor ) { return this . monitor . stop ( ) ; } } param message param reply handleCommand ( message , reply ) { const command = message . command ; const args = message . args ; switch ( command ) { case 'start' : this . startApp ( args ) . then ( ( ) => { reply ( { data : `${ args . appName } started successfully! Run command [ pandora log ${ args . appName } ] to get more information` } ) ; } ) . catch ( err => { reply ( { error : `${ args . appName } started failed, ${ err && err . toString ( ) }` } ) ; } ) ; break ; case 'stopAll' : this . stopAllApps ( ) . then ( ( ) => { reply ( { data : `all apps stopped successfully!` } ) ; } ) . catch ( err => { reply ( { error : `all apps stopped failed, ${ err && err . toString ( ) }` } ) ; } ) ; break ; case 'stopApp' : this . stopApp ( args . appName ) . then ( ( ) => { reply ( { data : `${ args . appName } stopped successfully!` } ) ; } ) . catch ( err => { reply ( { error : `${ args . appName } stopped failed, ${ err && err . toString ( ) }` } ) ; } ) ; break ; case 'restart' : this . stopApp ( args . appName ) . then ( diedApp => { return this . startApp ( diedApp . appRepresentation ) ; } ) . then ( ( ) => { reply ( { data : `${ args . appName } restarted successfully!` } ) ; } ) . catch ( err => { reply ( { error : `${ args . appName } restarted failed, ${ err && err . toString ( ) }` } ) ; } ) ; break ; case 'exit' : this . stop ( ) . then ( ( ) => { process . exit ( 0 ) ; } ) ; break ; case 'list' : const introspection = this . getIntrospection ( ) ; return introspection . listApplication ( ) . then ( ( data ) => { reply ( { data } ) ; } ) . catch ( ( error ) => { reply ( { error } ) ; } ) ; } } public getIntrospection ( ) { if ( ! this . introspection ) { this . introspection = new DaemonIntrospection ( this ) ; } return this . introspection ; } }