scalra
Version:
node.js framework to prototype and scale rapidly
523 lines (381 loc) • 12.6 kB
JavaScript
//
//
// component.js
//
// Component Management
//
// 2012-06-04 initial version
//
// 2012-06-11 rename from step to component
// 2012-07-06 add icAppConnector
// cache of all components
var l_components = {};
// check whether a component is available
exports.isInstalled = function (name) {
return l_components.hasOwnProperty(name);
};
// TODO: remove usage of SR.Settings.FRONTIER.getHostAddress()
//-----------------------------------------
// get predefined step for creating log files
// NOTE: this is replaced by /modules/log.js
// l_components['Log'] = exports.Log = function (path, log_name) {
// var stepLog = {
// name: 'Log',
// // define the init step
// start : function (onDone) {
// if (log_name.match(/[a-z]*$/) && log_name.match(/[a-z]*$/)[0]) {
// log_name = log_name.match(/[0-9a-zA-Z]*$/)[0];
// }
// //create log name
// // universal ISO format
// //var log_id = new Date().toISOString();
// // use local ISO format
// var log_id = UTIL.localISOString(new Date());
// log_id = log_id.replace(/:/g, '-');
// var fullpath = SR.path.join(path, '..', 'log');
// LOG.sys('log path: ' + fullpath, 'SR.Component');
// // store for later use (useful in notifying monitor)
// SR.Settings.SERVER_INFO.log = log_name + '.' + log_id;
// var debug_file = SR.Settings.SERVER_INFO.log + '.log';
// var error_file = SR.Settings.SERVER_INFO.log + '.err';
// SR.Log.createLog(fullpath, debug_file,
// function (pID) {
// LOG.setLogHandle(pID);
// SR.Log.createLog(fullpath, error_file,
// function (id) {
// LOG.setLogHandle(id, 'error');
// UTIL.safeCall(onDone);
// },
// function () {
// UTIL.safeCall(onDone);
// }
// );
// },
// onDone
// );
// },
// // define the stop procedure
// stop: function (onDone) {
// // dispose log file
// SR.Log.disposeAllLogs(onDone);
// }
// }
// return stepLog;
// }
// DB
l_components['DB'] = exports.DB = function (collection_names, shutdown_if_fail) {
var stepDB = {
name: 'DB',
start : function (onDone) {
SR.Module.init('DB', {collections: collection_names, shutdown_if_fail: shutdown_if_fail}, function (result) {
LOG.warn('onDone is called for DB Component...', 'SR.Component');
UTIL.safeCall(onDone, true);
});
},
stop : function (onDone) {
SR.Module.dispose('DB', onDone);
}
};
return stepDB;
};
//
// AppManager
//
// input:
//
l_components['AppManager'] = exports.AppManager = function () {
var stepAppManager = {
name: 'AppManager',
start : function (onDone) {
var manager_port = SR.Settings.FRONTIER.getHostAddress().port + SR.Settings.PORT_INC_APP_MANAGER;
LOG.warn('init AppManager... port: ' + manager_port, 'SR.Component');
SR.AppManager.init(manager_port,
function () {
LOG.sys('init AppManager done...', 'SR.Component');
UTIL.safeCall(onDone);
}
);
},
stop : function (onDone) {
LOG.sys('notify all app server to shutdown', 'SR.Component');
// notify all app servers to disconnect
// wait for replies from all apps
SR.Execute.stop([],
function () {
LOG.sys('shutdown all app servers', 'SR.Component');
// stop manager
LOG.sys('dispose AppManager...', 'SR.Component');
SR.AppManager.dispose(onDone);
}
);
}
};
return stepAppManager;
}; // end AppManager
//
// AppConnector
// init icAppConnector to connect to a AppManager
//
// input:
// ip_port: IP & port of the app manager,
// handler: custom handler for incoming messages
// info: {ip, port, eng_name, local_name}
//
// TODO: simplify?
l_components['AppConnector'] = exports.AppConnector = function () {
// TODO: if init fails, then all subsequent steps should not occur
var stepAppConnector = {
name: 'AppConnector',
start: function (onDone) {
// check if required settings exist
if (UTIL.userSettings('lobbyPort') === undefined) {
LOG.error('lobby port setting not available in settings.js file', 'SR.Component');
return UTIL.safeCall(onDone, false);
}
// IP & port setting of app manager
var manager_ip_port = {IP: SR.Settings.IP_LOBBY, port: UTIL.userSettings('lobbyPort')};
manager_ip_port.port += SR.Settings.PORT_INC_APP_MANAGER;
// prepare app server info (prepare default then see if we can overwrite)
var para = UTIL.clone(SR.Settings.SERVER_INFO);
// NOTE: currently, name is the server's directory name
// local_name is specified in the project's settings.js
// add legacy info
// TODO: check if still necessary or can be removed
var app_info = UTIL.userSettings('apps', para.name);
LOG.warn('app_info detected by AppConnector:', 'SR.Component');
LOG.warn(app_info);
if (app_info && app_info.local_name) {
para.local_name = app_info.local_name;
LOG.warn('load local name: ' + para.local_name, 'SR.Component');
}
// initialize AppConnector
LOG.sys('init SR.AppConnector... connecting AppManager', 'SR.Component');
SR.AppConnector.init(manager_ip_port, para,
function () {
if (SR.Settings.FRONTIER.isServerReady() === false) {
SR.Settings.FRONTIER.dispose();
return;
}
UTIL.safeCall(onDone);
}
);
},
stop: function (onDone) {
// disconnect from app manager
LOG.sys('disconnect from app manager...', 'SR.Component');
SR.AppConnector.dispose(onDone);
}
}; // end stepAppConnector
return stepAppConnector;
}; // end AppConnector
//
// REST Service
//
// input:
// port: port to listen for RESTful requests
//
// REST Service
l_components['REST'] = exports.REST = function (type, handle_list, port) {
// default to HTTP
type = type || 'HTTP';
var stepRESTService = {
name: 'REST-' + type,
start : function (onDone) {
// convert port to numerical value
if (port && typeof port === 'string')
port = parseInt(port);
var REST_port = port ||
SR.Settings.FRONTIER.getHostAddress().port +
(type === 'HTTPS' ? SR.Settings.PORT_INC_HTTPS : SR.Settings.PORT_INC_HTTP);
LOG.sys('init icREST...type: ' + type + ' port: ' + REST_port, 'SR.Component');
// start REST server given type, port, keys
var server = SR.REST.init(type, REST_port, UTIL.userSettings('keys'));
// store server
//l_components[type] = server;
// set default REST handlers
SR.REST.addHandler(SR.REST.Handler);
//SR.REST.addHandler('REST_upload.js');
SR.REST.addHandler('REST_web.js');
// NOTE: this has been moved to monitor server
//SR.REST.addHandler('REST_execute.js');
// set custom REST handlers
if (handle_list && handle_list.length > 0) {
for (var i=0; i < handle_list.length; i++)
SR.REST.addHandler(handle_list[i]);
}
UTIL.safeCall(onDone);
},
stop : function (onDone) {
LOG.sys('dispose icREST [' + type + ']...', 'SR.Component');
// NOTE: type will determine which kind of server to stop (HTTP or HTTPS)
SR.REST.dispose(type);
UTIL.safeCall(onDone);
}
};
return stepRESTService;
}; // end REST
//
// FB Connect Extension
//
// input:
// para: parameters for the FB app, including:
// app_id: FB-specific app ID
// app_secret: FB-specific app secret
// app_url: the actual application URL after login is done
// FB Connect Extension
l_components['FB'] = exports.FB = function () {
var step = {
name: 'FB',
start : function (onDone) {
var app_list = UTIL.userSettings('FB');
if (app_list === undefined) {
LOG.error('missing FB field in settings.js', 'SR.Component');
return UTIL.safeCall(onDone);
}
var keys = UTIL.userSettings('keys');
var FB_port = SR.Settings.FRONTIER.getHostAddress().port +
(keys !== undefined ? SR.Settings.PORT_INC_HTTPS : SR.Settings.PORT_INC_HTTP);
var domain = UTIL.userSettings('domain');
if (domain === undefined) {
LOG.error('"domain" setting not found in settings.js', 'SR.Component');
return UTIL.safeCall(onDone);
}
LOG.warn('init icFB...port: ' + FB_port + ' domain: ' + domain, 'SR.Component');
SR.SNS.FB.start(FB_port, domain, keys, function () {
// add individual apps to FB extension
for (var i=0; i < app_list.length; i++)
SR.SNS.FB.add(app_list[i]);
UTIL.safeCall(onDone);
});
},
stop : function (onDone) {
LOG.sys('dispose icFB...', 'SR.Component');
SR.SNS.FB.stop();
UTIL.safeCall(onDone);
}
};
return step;
}; // end FB
//
// socket.io Service
//
// input:
// port: port to listen for socket.io requests
//
// socket.io Service
l_components['SocketIO'] = exports.SocketIO = function (type) {
type = type || 'HTTP';
var step = {
name: 'SocketIO-' + type,
start : function (onDone) {
var port = SR.Settings.FRONTIER.getHostAddress().port + SR.Settings.PORT_INC_SOCKETIO;
console.log('port listening on - ',port);
// check if we'll use secured socket.io
var options = undefined;
if (type === 'HTTPS' && SR.Keys) {
options = {
key: SR.Keys.privatekey,
cert: SR.Keys.certificate
};
}
// check if we'll use port or server to init socketio, depending on whether HTTP server exists
var server_or_port = undefined;
//if (l_components.hasOwnProperty(type)) {
if (SR.REST.server.hasOwnProperty(type)) {
LOG.warn('starting Socket.IO server under ' + type + ' server', 'SR.Component');
//server_or_port = l_components[type];
server_or_port = SR.REST.server[type];
} else {
LOG.warn('starting Socket.IO server wth port: ' + port, 'SR.Component');
server_or_port = port;
}
// pass frontier's event dispatcher to handle incoming events
// TODO: better approach?
//SR.SocketIO.start(server_or_port, SR.Settings.FRONTIER.getConnectionHandler(), function () {
SR.SocketIO.start(server_or_port, function () {
UTIL.safeCall(onDone);
}, options);
},
stop : function (onDone) {
LOG.sys('dispose icSocketIO...', 'SR.Component');
SR.SocketIO.stop();
UTIL.safeCall(onDone);
}
};
return step;
}; // end SocketIO
//
// SockJS Service
//
// input:
// type: type of server ('HTTP' 'HTTPS')
//
// SockJS Service
l_components['SockJS'] = exports.SockJS = function (type) {
type = type || 'HTTP';
var step = {
created : false,
name: 'SockJS-' + type,
start : function (onDone) {
var http_server = undefined;
//if (l_components.hasOwnProperty(type)) {
if (SR.REST.server.hasOwnProperty(type)) {
LOG.sys('starting SockJS [' + type + '] server', 'SR.Component');
http_server = SR.REST.server[type];
} else {
var errmsg = 'an HTTP or HTTPS server must be started first';
LOG.error(errmsg, 'SR.Component');
return UTIL.safeCall(onDone, errmsg);
}
SR.SockJS.start(http_server, function (s) {
// record server created
this.created = true;
LOG.warn('SockJS [' + type + '] server started', 'SR.Component');
UTIL.safeCall(onDone);
});
},
stop : function (onDone) {
LOG.sys('dispose SockJS [' + type + '] server...', 'SR.Component');
if (this.created === true)
SR.SockJS.stop(type);
else
LOG.warn('SockJS [' + type + '] server not created, cannot dispose', 'SR.Component');
UTIL.safeCall(onDone);
}
};
return step;
}; // end SockJS
//
// streaming server Service
//
// input:
// port: port to listen for socket.io requests
//
// streaming server Service
l_components['Stream'] = exports.Stream = function () {
var stream_server = undefined;
var step = {
name: 'Stream',
start : function (onDone) {
var base_port = SR.Settings.FRONTIER.getHostAddress().port;
var port_in = base_port + SR.Settings.PORT_INC_STREAM_IN;
var port_out = base_port + SR.Settings.PORT_INC_STREAM_OUT;
LOG.sys('init SR.Stream, port_in: ' + port_in + ' port_out: ' + port_out, 'SR.Component');
// pass frontier's event dispatcher to handle incoming events
// TODO: better approach?
stream_server = new SR.Stream({
IN_PORT: port_in,
OUT_PORT: port_out
});
stream_server.start();
UTIL.safeCall(onDone);
},
stop : function (onDone) {
LOG.sys('dispose SR.Stream...', 'SR.Component');
if (stream_server)
stream_server.stop();
UTIL.safeCall(onDone);
}
};
return step;
}; // end Stream