endpointjs
Version:
Endpoint.js enables modules within a web application to discover and use each other, whether that be on the same web page, other browser windows and tabs, iframes, servers and web workers in a reactive way by providing robust discovery, execution and stre
227 lines (204 loc) • 8.39 kB
JavaScript
/*
* (C) 2016
* Booz Allen Hamilton, All rights reserved
* Powered by InnoVision, created by the GIAT
*
* Endpoint.js was developed at the
* National Geospatial-Intelligence Agency (NGA) in collaboration with
* Booz Allen Hamilton [http://www.boozallen.com]. The government has
* "unlimited rights" and is releasing this software to increase the
* impact of government investments by providing developers with the
* opportunity to take things in new directions.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* jshint -W097 */
/* globals __filename */
;
var adapter = require('./adapter/adapter'),
client = require('./facade/client'),
query = require('./facade/query'),
facadeManager = require('./facade-manager'),
appUtils = require('../util/appUtils'),
log = appUtils.getLogger(__filename);
module.exports = Api;
/**
* The Api class is added to the window and exposed as 'window.endpoint'. It is
* the main entry interface to call or execute methods within Endpoint.js
* @param {EndpointManager} endpointManager - An instance of the EndpointManager class
* @param {Configuration} config - system configuration
* @constructor
*/
function Api(endpointManager, config) {
if (!(this instanceof Api)) { return new Api(endpointManager, config); }
// Sender ID for this Endpoint.js.
this._id = config.get('instanceId');
// Used to pass to new instances of facade, query and adapter
this._endpointManager = endpointManager;
log.log(log.DEBUG, 'API Layer initialized');
}
/**
* Return the Endpoint.js instance id for this API.
* @returns {*}
*/
Api.prototype.getInstanceId = function() {
return this._id;
};
/**
* Return the endpoint manager
* @returns {*}
*/
Api.prototype.getEndpointManager = function() {
return this._endpointManager;
};
/**
* This function will return the configuration used
* to initially setup Endpoint.js. This is useful in order
* to add new links, remove links, or to add new sockets, workers, or windows
* to existing links.
*/
Api.prototype.getConfiguration = function() {
return this._endpointManager.getConfiguration();
};
/**
* Search the registry and send out a request for a specific adapter name,
* returing the created facade to the application.
* @param name - name of the adapter to look for.
* @param version - version of the adapter to look for.
* @param {Object} [settings] - additional parameters
* @param {Object} [settings.criteria] - options passed to the adapter's resolver
* @param {String} [settings.neighborhood] - how wide of a request to make (default to group)
* @param {String} [settings.bridgeId] - send query to only links that are on this bridge
* @param {String} [settings.hostId] - send query only to this host
* @param {Boolean} [settings.tryForever] - whether to continue sending out bus messages until the adapter is found
*/
Api.prototype.createQuery = function(name, version, settings) {
settings = settings || {};
// Create a facade.
return query(
this._endpointManager,
{
name: name,
version: version,
criteria: settings.criteria || {},
neighborhood: settings.neighborhood || 'local',
tryForever: settings.hasOwnProperty('tryForever') ? settings.tryForever : true,
bridgeId: settings.bridgeId,
hostId: settings.hostId
}
);
};
/**
* Search the registry and send out a request for a specific adapter name,
* returning the created facade to the application. Additionally,
* the request for adapters will be limited to internal servers only.
* @param name - name of the adapter to look for.
* @param version - version of the adapter to look for.
* @param {Object} [settings] - additional parameters
* @param {Object} [settings.criteria] - see createQuery.
* @param {String} [settings.neighborhood] - how wide of a request to make (default to group)
* @param {Object} [settings.api] - use the given api (from createQuery) instead of querying
* @param {String} [settings.bridgeId] - send query to only links that are on this bridge
* @param {String} [settings.hostId] - send query only to this host
* @param {Boolean} [settings.tryForever] - whether to continue sending out bus messages until the adapter is found
*/
Api.prototype.createFacade = function(name, version, settings) {
settings = settings || {};
// Create a client. The client manages the connection to the client instance,
// as well as events.
var clientInstance = client(
this._endpointManager,
{
name: name,
version: version
}
);
// Create the initial parent facade
var facade = clientInstance.createFacadeInstance(name);
// If someone closes the facade, close the client. The facade is
// already attached to the client instance via the above command to
// create it. So, they are circularly dependent.
facade.attachEndpoint(clientInstance, false);
// This function will take the given api and tell the
// client to connect to it.
function connectClient(api) {
clientInstance.connect(api.address, api.id, api.neighborhood, facade.getId());
}
// If we already have an api defined, then use it, otherwise create a query.
if (!settings.api) {
var query = this.createQuery(name, version, settings);
query.on('api', function(api) {
query.close();
connectClient(api);
});
query.on('timeout', function() {
log.log(log.WARN, 'A facade timed out for %s', this);
facade.emit('timeout');
facade.close();
});
// If someone closes the facade, then close the query.
facade.attachEndpoint(query);
}
else {
connectClient(settings.api);
}
return facade;
};
/**
* Create and manage multiple facades
*/
Api.prototype.manageFacades = function() {
var facadeMgr = facadeManager(this);
try {
for (var i = 0; i < arguments.length; i++) {
var item = arguments[i];
if (item.length < 2) {
facadeMgr.close();
throw new Error('Input error, invalid number of arguments for facade manager');
}
facadeManager.prototype.addFacade.apply(facadeMgr, item);
}
}
catch (e) {
log.log(log.ERROR, 'Could not create facade manager: %s, stack = %s', e.message, e.stack);
facadeMgr.close();
throw e;
}
return facadeMgr;
};
/**
* This function is used to register the given object as an adapter within
* the Endpoint.js registry. An adapter is returned.
* @param name - the exported name
* @param version - the version
* @param object - the object to export (functions starting with underscore are ignored)
* @param {Object} [settings] - additional parameters
* @param {Object} [settings.resolver] - object which specifies whether it responds to a request
* @param {Object} [settings.metadata] - criteria used to compare against
* @param {String} [settings.neighborhood] - how wide of a request to accept (default to group)
*/
Api.prototype.registerAdapter = function(name, version, object, settings) {
settings = settings || {};
// Create the adapter
return adapter(
this._endpointManager,
{
name: name,
version: version,
object: object,
neighborhood: settings.neighborhood || 'group',
resolver: settings.resolver,
metadata: settings.metadata || {}
}
);
};