blob: 8f4a3793da823740e77a44a9be43b763b000f397 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/**
* @fileoverview Vanadium Runtime
* @private
*/
var EE = require('eventemitter2').EventEmitter2;
var isBrowser = require('is-browser');
var Deferred = require('../lib/deferred');
var inherits = require('inherits');
var Server = require('../rpc/server');
var ServerRouter = require('../rpc/server-router');
var GranterRouter = require('../rpc/granter-router');
var Client = require('../rpc/client');
var Namespace = require('../naming/namespace');
var CaveatValidatorRegistry = require('../security/caveat-validator-registry');
var Principal = require('../security/principal');
var vlog = require('../lib/vlog');
var context = require('../context');
var SharedContextKeys = require('./shared-context-keys');
var vtrace = require('../vtrace');
var Controller =
require('../gen-vdl/v.io/x/ref/services/wspr/internal/app').Controller;
module.exports = {
init: init
};
/*
* Initialize a Vanadium Runtime.
* Runtime exposes entry points to create servers, client, blessing and other
* Vanadium functionality.
* @private
*/
function init(options, cb) {
var rt = new Runtime(options);
var promise = Promise.resolve(rt);
if (isBrowser) {
// In the browser, we must create the app instance in browspr. We send
// along the namespaceRoots and proxy, if they have been provided. If they
// are empty, the defaults from the extension options page will be used.
var settings = {
namespaceRoots: options.namespaceRoots || [],
proxy: options.proxy || ''
};
promise = promise.then(function(rt) {
var def = new Deferred();
rt._getProxyConnection().createInstance(settings, function(err) {
if (err) {
return def.reject(err);
}
def.resolve(rt);
});
return def.promise;
});
}
if (cb) {
promise.then(function(rt) {
cb(null, rt);
}, function(err) {
cb(err);
});
}
return promise;
}
/**
* Crash event.
* <p>Emitted when the runtime crashes in an unexpected way. Recovery from
* crash event requires restarting the application.<p>
* @event module:vanadium~Runtime#crash
*/
/**
* @summary
* Runtime exposes entry points to create servers, client, namespace client and
* other Vanadium functionality.
* @Description
* <p>This constructor should not be used directly, instead use
* [vanadium.init]{@link module:vanadium.init}</p>
*
* <p>Runtime is also an EventEmitter and emits
* [crash event]{@link module:vanadium~Runtime#event:crash} when it crashes in
* an unexpected way.</p>
*
* @property {string} accountName The accountName that the user associated to
* this runtime.
* @property {module:vanadium.security~Principal} principal The principal
* associated with this runtime. All operations that come from this
* runtime, including operations that come from
* [Servers]{@link module:vanadium.rpc~Server} and
* [Clients]{@link module:vanadium.rpc~Client}, will use this principal.
* @property {module:vanadium.security~CaveatValidatorRegistry} caveatRegistry
* Used to register custom first party caveat validators.
* @inner
* @memberof module:vanadium
* @constructor
*/
function Runtime(options) {
if (!(this instanceof Runtime)) {
return new Runtime(options);
}
EE.call(this);
this.accountName = options.accountName;
this._wspr = options.wspr;
var client = this.newClient();
this._controller = client.bindWithSignature(
'__controller', [Controller.prototype._serviceDescription]);
this.principal = new Principal(this.getContext(), this._controller);
this._name = options.appName;
this._language = options.language;
this.caveatRegistry = new CaveatValidatorRegistry();
}
inherits(Runtime, EE);
/**
* Closes the runtime, freeing all the related resources and stopping and
* unpublishing all the servers created in the runtime.
*
* @param {function} [cb] Gets called once the runtime is closed.
* @returns {Promise} Promise that will be resolved or rejected when runtime is
* closed.
*/
Runtime.prototype.close = function(cb) {
if (this._crashed) {
// NaCl plugin crashed. Shutting down will not work.
return process.nextTick(function() {
cb(new Error('Runtime crashed, can not shutdown gracefully.'));
});
}
var router = this._getRouter();
var proxy = this._getProxyConnection();
return router.cleanup().then(function() {
return proxy.close(cb);
});
};
/**
* Creates a new [Server]{@link module:vanadium.rpc~Server} instance.<br>
* Server allows one to create, publish and stop Vanadium services.
* @param {module:vanadium.rpc~Server~ServerOption} [serverOption] Optional
* server option that can be specified when creating a new server. serverOption
* can be created with the
* [vanadium.rpc.serverOption(opts)]{@link module:vanadium.rpc#serverOption}
* method.
* @return {module:vanadium.rpc~Server} A server instance.
*/
Runtime.prototype.newServer = function(serverOption) {
return new Server(this._getRouter(), serverOption);
};
/**
* Creates a new [Client]{@link module:vanadium.rpc~Client} instance.<br>
* Client allows one to bind to Vanadium names and call methods on them.
* @return {module:vanadium.rpc~Client} A Client instance.
*/
Runtime.prototype.newClient = function() {
return new Client(this._getProxyConnection());
};
/**
* Returns the root runtime [context]{@link module:vanadium.context.Context}<br>
* Context objects provide a number of features such as
* ability to provide configuration for requests such as timeout durations,
* tracing across requests for debugging, etc...<br>
* In order to provide these facilities context objects are required as the
* first parameter for client calls and also for requests that are incoming
* to servers.
* @return {module:vanadium.context.Context} The root runtime context.
*/
Runtime.prototype.getContext = function() {
if (this._rootCtx) {
return this._rootCtx;
}
var ctx = new context.Context();
ctx = ctx.withValue(SharedContextKeys.COMPONENT_NAME, this._name);
if (this._language) {
ctx = ctx.withValue(SharedContextKeys.LANG_KEY, this._language);
}
ctx = ctx.withValue(SharedContextKeys.RUNTIME, this);
ctx = vtrace.withNewStore(ctx);
ctx = vtrace.withNewTrace(ctx);
this._rootCtx = ctx;
return ctx;
};
/**
* <p>Returns a [namespace]{@link module:vanadium.naming~Namespace} client.</p>
* <p>Namespace client enables interactions with the Vanadium namespace such as
* globbing, mounting, setting permissions and other name related operations.
* </p>
* @return {module:vanadium.naming~Namespace} A namespace client instance.
*/
Runtime.prototype.namespace = function() {
this._ns = this._ns || new Namespace(this.newClient(),
this.getContext());
return this._ns;
};
/**
* Get or creates a new proxy connection
* @return {ProxyConnection} A proxy connection
* @private
*/
Runtime.prototype._getProxyConnection = function() {
if (this._proxyConnection) {
return this._proxyConnection;
}
var ProxyConnection;
if (this._wspr) {
vlog.logger.info('Using WSPR at: %s', this._wspr);
ProxyConnection = require('../proxy/websocket');
this._proxyConnection = new ProxyConnection(this._wspr);
} else {
vlog.logger.info('Using the Vanadium Extension\'s NaCl WSPR');
ProxyConnection = require('../proxy/nacl');
this._proxyConnection = new ProxyConnection();
}
// relay crash event from proxy
var self = this;
this._proxyConnection.on('crash', function(e) {
self._crashed = true;
self.emit('crash', e);
});
return this._proxyConnection;
};
/**
* Get or creates a router
* @return {ServerRouter} A router
* @private
*/
Runtime.prototype._getRouter = function() {
if (!this._router) {
this._router = new ServerRouter(
this._getProxyConnection(),
this._name,
this.getContext(),
this._controller,
this.caveatRegistry);
}
return this._router;
};
/**
* Get or creates a granter router
* @return {GranterRouter} A granter router
* @private
*/
Runtime.prototype._getGranterRouter = function() {
if (!this._granterRouter) {
this._granterRouter = new GranterRouter(
this._getProxyConnection(),
this.getContext());
}
return this._granterRouter;
};