/*
UnityBase startup script
this script initializes working thread JavaScript context and is called by server fo each thread.
In case UB runs in batch mode, script is called once - for main thread only
*/
/**
* The UB namespace (global object) encapsulates some classes, singletons, and utility methods provided by UnityBase server.
* @namespace UB
*/
UB = {};
/**
* If we are in UnityBase server scripting (both -f or server thread) this property is true, if in browser - undefined or false.
* Use it for check execution context in scripts, shared between client & server.
* To check we are in server thread use process.isServer.
* @readonly
*/
UB.isServer = true; //Object.defineProperty(UB, 'isServer', {enumerable: true, value: true} );
(function (nativeProcess) {
this.global = this;
//noinspection JSUndeclaredVariable
/**
* Information about current executable. Accessible via global variable `process`
* @type {Process}
* @global
*/
process = nativeProcess;
/**
* Current working directory
* @return {string|String}
*/
process.cwd = function () {
return nativeProcess.startupPath;
};
/**
* List of loaded via `require` modules
* @private
* @type {Array<string>}
*/
process.moduleLoadList = [];
/**
* This function is just to be compatible with node.js
* @param {Function} cb Callback (called immediately in UB)
*/
process.nextTick = function(cb){
cb();
};
/**
* The main executable full path (excluding .exe file name)
* @type {String}
* @readonly
*/
process.execPath = process.binPath;
// process & module function is documented inside _UBCommonGlobals.js
function startup() {
//noinspection JSUndeclaredVariable
/**
* Put something to log with log levels depending on method
* @global
* @type {Console}
*/
console = NativeModule.require('console');
var Module = NativeModule.require('module');
Module.call(global, ['.']);
process.mainModule = global;
//noinspection JSUndeclaredVariable
/**
* Load a module. Acts like a <a href="http://nodejs.org/api/modules.html">Node JS</a> require, with 4 difference:
*
* - **Core modules** list is: `["fs", "util", "path", "assert", "module", "console" and "events"]` - this modules always loaded from `bin\modules` folder
* - In case of `moduleName` is **RELATIVE** (not start from `./` `../` or `X:\` where X is drive letter) lookup order is:
* - `currentModulePath + \modules`
* - `currentModulePath + \node_modules`
* - `bin\modules`
* - `process.cwd()`
* - in case UnityBase run in production (`!process.isDebug`) and package.json contain `main_min` entry it value will be used as a module entry point<br>
* Placing minimized js into `main_min` help to prevent memory overflow;
* - `require` know about UnityBase **models**. In **server thread** context, in case `moduleName` start from `models/ModelName` require search for module inside `ModelName.path` folder:
*
* require('models/UBS/public/UBReport');
*
* will search in domain config (ubConfig.json) path for `UBS` model and perform request relative to this folder, i.e. load `D:\projects\UnityBase\models\UBS\public\UBReport.js` in my case.
*
* *In case you need to debug from there module is loaded set OS Environment variable*
* `>SET NODE_DEBUG=modules` *and restart server - require will put to debug log all information about how module are loaded.* Do not do this on production, of course :)
*
* @global
* @method
* @param {String} moduleName
* @returns {*}
*/
require = Module.prototype.require;
//noinspection JSUndeclaredVariable
/**
* Instance of lodash library. Follow <a href="http://lodash.com/docs">this link</a> to see documentation on the official site.
* @global
* @type {lodash}
*/
_ = require('lodash');
// Used in Pdf for binary data in fonts. please do not remove this definition
var base64 = require('base64.js');
//noinspection JSUndeclaredVariable
/**
* The same as <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64.atob">window.atob</a> in browser. Warning! slow implementation.
* @method atob
*/
atob = base64.atob;
//noinspection JSUndeclaredVariable
/**
* The same as <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64.btoa">window.btoa</a> in browser. Warning! slow implementation.
* @method btoa
*/
btoa = base64.btoa;
var EventEmitter = NativeModule.require('events').EventEmitter;
// add EventEmitter to process object
EventEmitter.call(process);
var util = NativeModule.require('util');
util._extend(process, EventEmitter.prototype);
/**
* Server-side Abort exception. To be used in server-side logic in case of HANDLED
* exception. This errors logged using "Error" log level to prevent unnecessary
* EXC log entries.
*
* // UB client will show message inside <<<>>> to user (and translate it using UB.i18n)
* throw new UB.UBAbort('<<<textToDisplayForClient>>>');
* //for API methods we do not need <<<>>>
* throw new UB.UBAbort('wrongParameters');
*
* @param {String} [message] Message
* @extends {Error}
*/
UB.UBAbort = function UBAbort(message) {
this.name = 'UBAbort';
this.code = 'UBAbort';
this.message = message || 'UBAbortError';
this.stack = (new Error()).stack;
};
UB.UBAbort.prototype = new Error();
UB.UBAbort.prototype.constructor = UB.UBAbort;
var repositoryConstructor = require('Repository'); // for backward compatibility with UB 1.7
/**
* Create new instance of {@link ServerRepository}
*
* @param {String} entityName
* @param {Object} [cfg]
* @param {UBConnection} [connection] Pass in case of remote UnityBase server connection.
* @returns {ServerRepository}
*/
UB.Repository = function (entityName, cfg, connection) {
connection = connection || global.conn;
if (connection){
return new repositoryConstructor(connection, entityName, cfg);
} else {
return new repositoryConstructor(null, entityName, cfg);
}
};
if (process.isServer || process.isWebSocketServer){
if (process.isServer) {
// add EventEmitter to Session object
EventEmitter.call(Session);
util._extend(Session, EventEmitter.prototype);
/**
* Called by UB.exe in server thread during Domain initialization just after all scopes for entity is created but before entities modules (entityName.js) is evaluated
* @private
*/
UB.initEventEmitter = function(){
var
ettCnt = App.domain.count, i, n, obj;
var __preventDefault = false;
//add eventEmitter to application object
obj = App;
EventEmitter.call(obj);
util._extend(obj, EventEmitter.prototype);
App.emitWithPrevent = function(type, req, resp){
__preventDefault = false;
this.emit(type, req, resp);
return __preventDefault;
};
/**
* Accessible inside app-level `:before` event handler. Call to prevent default method handler.
* In this case developer are responsible to fill response object, otherwise HTTP 400 is returned.
* @memberOf App
*/
App.preventDefault = function(){
__preventDefault = true;
};
//add eventEmitter to all entities
for(i=0; i<ettCnt; i++){
//console.log(App.domain.items[i].name);
n = App.domain.items[i].name;
if (obj = global[n]){
// add EventEmitter to entity scope object
EventEmitter.call(obj);
util._extend(obj, EventEmitter.prototype);
}
}
};
var appBinding = process.binding('app');
/**
* Register a server endpoint. By default access to endpoint require authentication
* @example
*
* // Write a custom request body to file FIXTURES/req and echo file back to client
* // @param {THTTPRequest} req
* // @param {THTTPResponse} resp
* //
* function echoToFile(req, resp) {
* var fs = require('fs');
* fs.writeFileSync(FIXTURES + 'req', req.read('bin'));
* resp.statusCode = 200;
* resp.writeEnd(fs.readFileSync(FIXTURES + 'req', {encoding: 'bin'}));
* }
* App.registerEndpoint('echoToFile', echoToFile);
*
* @param {String} endpointName
* @param {Function} handler
* @param {boolean} [requireAuthentication=true]
* @memberOf App
*/
App.registerEndpoint = function(endpointName, handler, requireAuthentication) {
if (!appBinding.endpoints[endpointName]) {
appBinding.endpoints[endpointName] = handler;
return appBinding.registerEndpoint(endpointName, requireAuthentication === undefined ? true : requireAuthentication);
}
};
var argv = require('cmd/argv');
/**
* Server configuration (result of argv.getServerConfiguration() execution)
* In this case developer are responsible to fill response object, otherwise HTTP 400 is returned.
* @type {Object}
* @memberOf App
*/
App.serverConfig = undefined;
try {
App.serverConfig = argv.getServerConfiguration();
} catch (e){
console.error(e);
}
/**
* @param {String} methodName
* @method addAppLevelMethod
* @deprecated Use {@link App.registerEndpoint} instead
* @memberOf App
*/
App.addAppLevelMethod = function(methodName) {
if (!appBinding.endpoints[methodName]) {
appBinding.endpoints[methodName] = global[methodName];
return appBinding.registerEndpoint(methodName, true);
}
};
/**
* @param {String} methodName
* @method serviceMethodByPassAuthentication
* @deprecated Use {@link App.registerEndpoint} instead
* @memberOf App
*/
App.serviceMethodByPassAuthentication = function(methodName){
return appBinding.setEndpointByPassAuthentication(methodName);
};
var sessionBinding = process.binding('session');
/**
* Create new session for userID
* @deprecated use runAsUser instead this
* @method
* @param {Number} userID ID of user
* @param {String} [secret] secret word. If defined then session secretWord is `JSON.parse(returns).result+secret`
* @returns {String} JSON string like answer on auth request
*/
Session.setUser = sessionBinding.switchUser;
/**
* Call function as admin.
* Built-in "always alive"(newer expired) `admin` session is always created when the application starts,
* so this is very cheap method - it will not trigger Session.login event every time context is switched (Session.setUser and Session.runAsUser does)
* Can be used in scheduled tasks, not-authorized methods, etc. to obtain a `admin` Session context
* @param {Function} call Function to be called in admin context
* @returns {*}
*/
Session.runAsAdmin = function(call){
var result;
sessionBinding.switchToAdmin();
result = call();
sessionBinding.switchToOriginal();
return result;
};
/**
* Call function as custom user.
* New session will be created. Will fire `login` event.
* @param userID ID of user
* @param call Function to be called in user's session.
* @returns {*}
*/
Session.runAsUser = function(userID, call){
var result;
sessionBinding.switchUser(userID);
result = call();
sessionBinding.switchToOriginal();
return result;
};
}
var _ubNotifierInstance = process.isWebSocketEnabled ? undefined : null;
var JsonMessagesProtocol = require('WebSockets').protocols.JsonMessages;
/**
* Return a instance of {@link JsonMessagesProtocol} named `ubNotifier` for Server<->Client communication using WebSocket
*
* In case `ubNotifier` protocol is not registered during WebSocket thread initialization
* or not configured in config - will return `null`
*
* Returned {@link JsonMessagesProtocol} instance methods is limited
* by {@link WSProtocol#getUserSessions}, {@link WSProtocol#sendTo} and {@link WSProtocol#broadcast}
*
* See {@tutorial web_sockets.md } for detailed descripiuon
*
* @return {JsonMessagesProtocol}
*/
UB.getWSNotifier = function(){
if (_ubNotifierInstance || _ubNotifierInstance===null)
return _ubNotifierInstance;
_ubNotifierInstance = new JsonMessagesProtocol('ubNotifier');
return _ubNotifierInstance;
}
} else {
/*
* Create connection to server and expose it as a global `conn` variable.. Login operation is performed on first request what need auth.
* FOR A GUI SERVER ONLY
* @return {Boolean} success or not
*/
UB.autologon = function(serverURL, user, pwd){
var
UBConnection = require('UBConnection'), conn;
function createRequestAuthParams (){
return function() { return {login: user, password: pwd} };
}
if (!/http:\/\/|https:\/\//.test(serverURL)) serverURL = 'http://'+serverURL; //fix URL parse
conn = global.conn = new UBConnection({
URL: serverURL
});
conn.onRequestAuthParams = createRequestAuthParams();
return true;
};
}
}
/**
* Creates namespaces to be used for scoping variables and classes so that they are not global.
*
* UB.ns('DOC.Report');
* DOC.Report.myReport = function() { ... };
*
* @param {String} namespacePath
* @return {Object} The namespace object.
*/
UB.ns = function (namespacePath) {
var root = global, parts, part, j, subLn;
parts = namespacePath.split('.');
for (j = 0, subLn = parts.length; j < subLn; j++) {
part = parts[j];
if (!root[part]) {
root[part] = {};
}
root = root[part];
}
return root;
};
var FORMAT_RE = /\{(\d+)}/g;
/**
* Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
* token must be unique, and must increment in the format {0}, {1}, etc.
* @example
*
* var s = UB.format('{1}/lang-{0}.js', 'en', 'locale');
* // s now contains the string: ''locale/lang-en.js''
*
* @param {String} stringToFormat The string to be formatted.
* @param {...*} values The values to replace tokens `{0}`, `{1}`, etc in order.
* @return {String} The formatted string.
*/
UB.format = function(stringToFormat, values) {
var args = _.toArray(arguments).slice(1);
return stringToFormat.replace(FORMAT_RE, function(m, i) {
return args[i];
});
};
function evalScript(name) {
var Module = NativeModule.require('module');
var path = NativeModule.require('path');
var cwd = process.cwd();
var module = new Module(name);
module.filename = path.join(cwd, name);
module.paths = Module._nodeModulePaths(cwd);
var script = process._eval;
if (!Module._contextLoad) {
var body = script;
script = 'global.__filename = ' + JSON.stringify(name) + ';\n' + 'global.exports = exports;\n' + 'global.module = module;\n' + 'global.__dirname = __dirname;\n' + 'global.require = require;\n' + 'return require("vm").runInThisContext(' + JSON.stringify(body) + ', ' + JSON.stringify(name) + ', true);\n';
}
var result = module._compile(script, name + '-wrapper');
if (process._print_eval) console.log(result);
}
function NativeModule(id) {
this.filename = id + '.js';
this.id = id;
this.exports = {};
this.loaded = false;
}
NativeModule._source = {
fs: relToAbs(process.binPath, '.\\modules\\fs.js'),
util: relToAbs(process.binPath, '.\\modules\\util.js'),
path: relToAbs(process.binPath, '.\\modules\\path.js'),
assert: relToAbs(process.binPath, '.\\modules\\assert.js'),
module: relToAbs(process.binPath, '.\\modules\\module.js'),
console: relToAbs(process.binPath, '.\\modules\\console.js'),
events: relToAbs(process.binPath, '.\\modules\\events.js')
};
NativeModule._cache = {};
NativeModule.require = function (id) {
if (id == 'native_module') {
return NativeModule;
}
var cached = NativeModule.getCached(id);
if (cached) {
return cached.exports;
}
if (!NativeModule.exists(id)) {
throw new Error('No such native module ' + id);
}
process.moduleLoadList.push('NativeModule ' + id);
var nativeModule = new NativeModule(id);
nativeModule.cache();
nativeModule.compile();
return nativeModule.exports;
};
NativeModule.getCached = function (id) {
if (NativeModule._cache.hasOwnProperty(id)) {
return NativeModule._cache[id]
} else {
return null;
}
};
NativeModule.exists = function (id) {
return NativeModule._source.hasOwnProperty(id);
};
NativeModule.getSource = function (id) {
return loadFile(NativeModule._source[id]);
};
NativeModule.wrap = function (script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ', '\n});'
];
NativeModule.prototype.compile = function () {
var source = NativeModule.getSource(this.id);
source = NativeModule.wrap(source);
var fn = runInThisContext(source, this.filename, true);
fn(this.exports, NativeModule.require, this, this.filename);
this.loaded = true;
};
NativeModule.prototype.cache = function () {
NativeModule._cache[this.id] = this;
};
startup();
/* if (!process.isServer){
toLog('!!!!not server - run startup')
var Module = NativeModule.require('module');
Module._load(process.argv[1], null, true);
}*/
})(_process);