2016-05-03 22:50:21 +00:00
|
|
|
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
|
|
|
// Node module: loopback
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2015-03-27 13:45:37 +00:00
|
|
|
var juggler = require('loopback-datasource-juggler');
|
|
|
|
var remoting = require('strong-remoting');
|
|
|
|
var cls = require('continuation-local-storage');
|
|
|
|
var domain = require('domain');
|
|
|
|
|
|
|
|
module.exports = function(loopback) {
|
|
|
|
/**
|
|
|
|
* Get the current context object. The context is preserved
|
|
|
|
* across async calls, it behaves like a thread-local storage.
|
|
|
|
*
|
|
|
|
* @returns {ChainedContext} The context object or null.
|
|
|
|
*/
|
|
|
|
loopback.getCurrentContext = function() {
|
|
|
|
// A placeholder method, see loopback.createContext() for the real version
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Run the given function in such way that
|
|
|
|
* `loopback.getCurrentContext` returns the
|
|
|
|
* provided context object.
|
|
|
|
*
|
|
|
|
* **NOTE**
|
|
|
|
*
|
|
|
|
* The method is supported on the server only, it does not work
|
|
|
|
* in the browser at the moment.
|
|
|
|
*
|
|
|
|
* @param {Function} fn The function to run, it will receive arguments
|
|
|
|
* (currentContext, currentDomain).
|
|
|
|
* @param {ChainedContext} context An optional context object.
|
|
|
|
* When no value is provided, then the default global context is used.
|
|
|
|
*/
|
|
|
|
loopback.runInContext = function(fn, context) {
|
|
|
|
var currentDomain = domain.create();
|
|
|
|
currentDomain.oldBind = currentDomain.bind;
|
|
|
|
currentDomain.bind = function(callback, context) {
|
|
|
|
return currentDomain.oldBind(ns.bind(callback, context), context);
|
|
|
|
};
|
|
|
|
|
|
|
|
var ns = context || loopback.createContext('loopback');
|
|
|
|
|
|
|
|
currentDomain.run(function() {
|
|
|
|
ns.run(function executeInContext(context) {
|
|
|
|
fn(ns, currentDomain);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new LoopBackContext instance that can be used
|
|
|
|
* for `loopback.runInContext`.
|
|
|
|
*
|
|
|
|
* **NOTES**
|
|
|
|
*
|
|
|
|
* At the moment, `loopback.getCurrentContext` supports
|
|
|
|
* a single global context instance only. If you call `createContext()`
|
|
|
|
* multiple times, `getCurrentContext` will return the last context
|
|
|
|
* created.
|
|
|
|
*
|
|
|
|
* The method is supported on the server only, it does not work
|
|
|
|
* in the browser at the moment.
|
|
|
|
*
|
|
|
|
* @param {String} scopeName An optional scope name.
|
|
|
|
* @return {ChainedContext} The new context object.
|
|
|
|
*/
|
|
|
|
loopback.createContext = function(scopeName) {
|
|
|
|
// Make the namespace globally visible via the process.context property
|
|
|
|
process.context = process.context || {};
|
|
|
|
var ns = process.context[scopeName];
|
|
|
|
if (!ns) {
|
|
|
|
ns = cls.createNamespace(scopeName);
|
|
|
|
process.context[scopeName] = ns;
|
|
|
|
// Set up loopback.getCurrentContext()
|
|
|
|
loopback.getCurrentContext = function() {
|
|
|
|
return ns && ns.active ? ns : null;
|
|
|
|
};
|
|
|
|
|
|
|
|
chain(juggler);
|
|
|
|
chain(remoting);
|
|
|
|
}
|
|
|
|
return ns;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a chained context
|
|
|
|
* @param {Object} child The child context
|
|
|
|
* @param {Object} parent The parent context
|
|
|
|
* @private
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function ChainedContext(child, parent) {
|
|
|
|
this.child = child;
|
|
|
|
this.parent = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the value by name from the context. If it doesn't exist in the child
|
|
|
|
* context, try the parent one
|
|
|
|
* @param {String} name Name of the context property
|
|
|
|
* @returns {*} Value of the context property
|
|
|
|
*/
|
|
|
|
ChainedContext.prototype.get = function(name) {
|
|
|
|
var val = this.child && this.child.get(name);
|
|
|
|
if (val === undefined) {
|
|
|
|
return this.parent && this.parent.get(name);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ChainedContext.prototype.set = function(name, val) {
|
|
|
|
if (this.child) {
|
|
|
|
return this.child.set(name, val);
|
|
|
|
} else {
|
|
|
|
return this.parent && this.parent.set(name, val);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ChainedContext.prototype.reset = function(name, val) {
|
|
|
|
if (this.child) {
|
|
|
|
return this.child.reset(name, val);
|
|
|
|
} else {
|
|
|
|
return this.parent && this.parent.reset(name, val);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function chain(child) {
|
|
|
|
if (typeof child.getCurrentContext === 'function') {
|
|
|
|
var childContext = new ChainedContext(child.getCurrentContext(),
|
|
|
|
loopback.getCurrentContext());
|
|
|
|
child.getCurrentContext = function() {
|
|
|
|
return childContext;
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
child.getCurrentContext = loopback.getCurrentContext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|