UNPKG

thali

Version:
395 lines (330 loc) 18.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: thalireplicationmanager.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: thalireplicationmanager.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>'use strict'; var ThaliEmitter = require('./thaliemitter'); var EventEmitter = require('events').EventEmitter; var inherits = require('util').inherits; var net = require('net'); var tcpMultiplex = require('./tcpmultiplex'); var validations = require('./validations'); var cryptomanager = require('./thalicryptomanager'); var deviceIdentityFlag = { noDeviceIdentitySet: 0, gettingDeviceIdentity: 1, deviceIdentityAvailable: 2 }; var PEER_AVAILABILITY_CHANGED = ThaliEmitter.events.PEER_AVAILABILITY_CHANGED; var NETWORK_CHANGED = ThaliEmitter.events.NETWORK_CHANGED; var CONNECTION_ERROR = ThaliEmitter.events.CONNECTION_ERROR; inherits(ThaliReplicationManager, EventEmitter); function ThaliReplicationManager(db, emitter) { if (!db) { throw new Error('db is required to inititialize the ThaliReplicationManager'); } EventEmitter.call(this); console.log('DB value for ThaliReplicationManager is: %s', JSON.stringify(db)); this._db = db; this._emitter = (emitter || new ThaliEmitter()); this.clearState(); } ThaliReplicationManager.events = { STARTING: 'starting', STARTED: 'started', STOPPING: 'stopping', STOPPED: 'stopped', START_ERROR: 'startError', STOP_ERROR: 'stopError', CONNECT_ERROR: 'connectError', DISCONNECT_ERROR: 'disconnectError', SYNC_ERROR: 'syncError', CONNECTION_SUCCESS: 'connectionSuccess' }; ThaliReplicationManager.prototype.clearState = function() { this._deviceName = ''; this._deviceIdentityFlag = deviceIdentityFlag.noDeviceIdentitySet; this._deviceIdentityListeners = []; this._peers = {}; this._replications = {}; this._clients = {}; this._isStarted = false; this._serverBridge = null; this._serverBridgePort = 0; this._isInRetry = {}; }; /** * Returns the current device's id if available. If not, the provided callback * function is saved and called when the id does become available. * @param {Function} cb the callback which returns the current device's id. */ ThaliReplicationManager.prototype.getDeviceIdentity = function (cb) { if(this._deviceIdentityFlag == deviceIdentityFlag.deviceIdentityAvailable) { cb(null, this._deviceName); return; } if(this._deviceIdentityFlag == deviceIdentityFlag.noDeviceIdentitySet) { this._deviceIdentityFlag = deviceIdentityFlag.gettingDeviceIdentity; // save the callback for future this._deviceIdentityListeners.push(cb); cryptomanager.getPublicKeyHash(function (err, publicKeyHash) { if (err) { this._deviceIdentityFlag = deviceIdentityFlag.noDeviceIdentitySet; } else { this._deviceName = publicKeyHash; this._deviceIdentityFlag = deviceIdentityFlag.deviceIdentityAvailable; } // save the list of device-identity-listeners locally and clear the // global list. this will avoid a potential race condition if one of // the callback functions tries to get the device identity again // immediately. var localCopyOfListeners = this._deviceIdentityListeners.slice(); this._deviceIdentityListeners = []; localCopyOfListeners.forEach(function (listener) { listener(err, publicKeyHash); }); }.bind(this)); return; } if(this._deviceIdentityFlag == deviceIdentityFlag.gettingDeviceIdentity) { // save the callback for future this._deviceIdentityListeners.push(cb); return; } throw new Error('deviceIdentityFlag is set to unknown state'); }; /** * Starts the Thali replication manager with the given port number and db name. * The device-id is obtained using the cryptomanager's API. * @param {Number} port the port number used for synchronization. * @param {String} dbName the name of the database. * @param {String} [deviceName] the optional name to advertise for the device */ ThaliReplicationManager.prototype.start = function (port, dbName, deviceName) { validations.ensureValidPort(port); validations.ensureNonNullOrEmptyString(dbName, 'dbName'); if (deviceName) { this._deviceName = deviceName; this._start(port, dbName); } else { this.getDeviceIdentity.call(this, function(err, publicKeyHash) { if(err) { this._isStarted = false; this.emit(ThaliReplicationManager.events.START_ERROR, err); return; } this._start(port, dbName); }.bind(this)); } }; ThaliReplicationManager.prototype._start = function (port, dbName) { this.emit(ThaliReplicationManager.events.STARTING); this._port = port; this._dbName = dbName; if (this._isStarted || !!this._serverBridge) { return this.emit( ThaliReplicationManager.events.START_ERROR, new Error('There is already an existing serverBridge instance.') ); } this._serverBridge = tcpMultiplex.muxServerBridge(port); this._serverBridge.listen(function () { this._serverBridgePort = this._serverBridge.address().port; this._emitter.addListener(PEER_AVAILABILITY_CHANGED, this._syncPeers.bind(this)); this._emitter.addListener(NETWORK_CHANGED, this._networkChanged.bind(this)); this._emitter.addListener(CONNECTION_ERROR, this._connectionError.bind(this)); this._emitter.startBroadcasting(this._deviceName, this._serverBridgePort, function (err) { if (err) { this._isStarted = false; this.emit(ThaliReplicationManager.events.START_ERROR, err); } else { this._isStarted = true; this.emit(ThaliReplicationManager.events.STARTED); } }.bind(this)); }.bind(this)); }; /** * Stops the Thali replication manager */ ThaliReplicationManager.prototype.stop = function () { console.log('Now in TRM stop'); console.log('State of this._isStarted: %s', this._isStarted); if (!this._isStarted) { throw new Error('.start must be called before stop'); } this.emit(ThaliReplicationManager.events.STOPPING); console.log('About to call stopBroadcasting'); this._emitter.stopBroadcasting(function (err) { console.log('Got callback from stopBroadcasting with err %s', err); this._emitter.removeAllListeners(PEER_AVAILABILITY_CHANGED); this._emitter.removeAllListeners(NETWORK_CHANGED); this._emitter.removeAllListeners(CONNECTION_ERROR); Object.keys(this._replications).forEach(function (key) { var item = this._replications[key]; item.from.cancel(); item.to.cancel(); }, this); this._serverBridge.close(); this._serverBridge = null; this._isStarted = false; this.clearState(); if (err) { this.emit(ThaliReplicationManager.events.STOP_ERROR, err); } else { this.emit(ThaliReplicationManager.events.STOPPED); } }.bind(this)); }; /** * Reacts to the networkChanged event to start or stop based upon its current status. */ ThaliReplicationManager.prototype._networkChanged = function (status) { if (!status.isAvailable &amp;&amp; this._isStarted) { return this.stop(); } if (status.isAvailable &amp;&amp; !this._isStarted) { return this.start(this._deviceName, this._port, this._dbName); } }; /** * Handles the connection errors from the native layer for synchronization * @param {Object} status A message containing the peerIdentifier for the connection error */ ThaliReplicationManager.prototype._connectionError = function (status) { this._syncRetry(status.peerIdentifier); }; /** * Synchronizes the peers from the peerAvailabiltyChanged event. * @param {Object} peers Peers to sync which contain the peerIdentifier, peerAvailable and peerName. */ ThaliReplicationManager.prototype._syncPeers = function (peers) { // Get a list of peers for later for checking if available this._peers = peers.reduce(function (acc, peer) { acc[peer.peerIdentifier] = peer; return acc; }, this._peers); peers.forEach(function (peer) { var existingReplication = this._replications[peer.peerIdentifier]; !existingReplication &amp;&amp; peer.peerAvailable &amp;&amp; this._syncPeer(peer.peerIdentifier); if (existingReplication &amp;&amp; !peer.peerAvailable) { var client = this._clients[peer.peerIdentifier]; if (client) { this._clients[peer.peerIdentifier].close(function (err) { if(err) { console.log('Client close error with error: %s', err); this.emit(ThaliReplicationManager.events.DISCONNECT_ERROR, err); } }); delete this._clients[peer.peerIdentifier]; } existingReplication.from.cancel(); existingReplication.to.cancel(); delete this._replications[peer.peerIdentifier]; this._emitter.disconnect(peer.peerIdentifier, function (err) { if (err) { console.log('Disconnect error with error: %s', err); this.emit(ThaliReplicationManager.events.DISCONNECT_ERROR, err); } }.bind(this)); } }, this); }; /** * Synchronizes a single peer with the given peer identifier * @param {String} peerIdentifier The peer identifier to synchronize with. */ ThaliReplicationManager.prototype._syncPeer = function (peerIdentifier) { var peer = this._peers[peerIdentifier]; if (!peer) { console.log('peerIdentifier not found: %s', peerIdentifier); return; } if (peer &amp;&amp; !peer.peerAvailable) { console.log('peerIdentifier not available %s', peerIdentifier); return; } this._emitter.connect(peer.peerIdentifier, function (err, port) { if (err) { console.log('Connect error with error: %s', err); this.emit(ThaliReplicationManager.events.CONNECT_ERROR, err); return setImmediate(this._syncRetry.bind(this, peerIdentifier)); } var client = tcpMultiplex.muxClientBridge(port, function (err) { if (err) { console.log('Error in mux client bridge %s', err); return setImmediate(this._syncRetry.bind(this, peerIdentifier)); } this._clients[peer.peerIdentifier] = client; client.listen(function () { var localPort = client.address().port; this.emit(ThaliReplicationManager.events.CONNECTION_SUCCESS, { peerIdentifier : peer.peerIdentifier, muxPort : localPort }); var remoteDB = 'http://localhost:' + localPort + '/db/' + this._dbName; var options = { live: true, retry: true }; this._replications[peer.peerIdentifier] = { from: this._db.replicate.from(remoteDB, options), to: this._db.replicate.to(remoteDB, options) }; }.bind(this)); }.bind(this)); }.bind(this)); }; /** * Retry a synchronization after a failure with the given peer identifier. This tears down * all the client replications so that we can have a clean sync retry * @param {String} peerIdentifier The peer identifier to retry the synchronization with. */ ThaliReplicationManager.prototype._syncRetry = function (peerIdentifier) { if (!this._isStarted) { return console.log('syncRetry called when not started'); } if (this._isInRetry[peerIdentifier]) { return console.log('peerIdentifier already in syncRetry %s', peerIdentifier); } this._isInRetry[peerIdentifier] = true; var existingClient = this._clients[peerIdentifier]; if (existingClient) { try { existingClient.close(); } catch (e) { console.log('Client close with error: %s', e); } delete this._clients[peerIdentifier]; } var existingReplication = this._replications[peerIdentifier]; if (existingReplication) { existingReplication.from.cancel(); existingReplication.to.cancel(); delete this._replications[peerIdentifier]; } this._emitter.disconnect(peerIdentifier, function (err) { if (err) { console.log('Disconnect error with error: %s', err); this.emit(ThaliReplicationManager.events.DISCONNECT_ERROR, err); } this._isInRetry[peerIdentifier] = false; this._syncPeer(peerIdentifier); }.bind(this)); }; module.exports = ThaliReplicationManager; </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-TCPServersManager.html">TCPServersManager</a></li><li><a href="module-thaliMobile.html">thaliMobile</a></li><li><a href="module-thaliMobileNative.html">thaliMobileNative</a></li><li><a href="module-thaliMobileNativeWrapper.html">thaliMobileNativeWrapper</a></li><li><a href="module-thaliNotificationAction.html">thaliNotificationAction</a></li><li><a href="module-thaliNotificationBeacons.html">thaliNotificationBeacons</a></li><li><a href="module-thaliNotificationClient.html">thaliNotificationClient</a></li><li><a href="module-thaliNotificationServer.html">thaliNotificationServer</a></li><li><a href="module-thaliPeerAction.html">thaliPeerAction</a></li><li><a href="module-thaliPeerDictionary.html">thaliPeerDictionary</a></li><li><a href="module-thaliPeerPoolInterface.html">thaliPeerPoolInterface</a></li><li><a href="module-ThaliWifiInfrastructure.html">ThaliWifiInfrastructure</a></li><li><a href="module-WifiBasedNativeMock.html">WifiBasedNativeMock</a></li></ul><h3>Externals</h3><ul><li><a href="external-_Mobile(_connect_)_.html">Mobile('connect')</a></li><li><a href="external-_Mobile(_discoveryAdvertisingStateUpdateNonTCP_)_.html">Mobile('discoveryAdvertisingStateUpdateNonTCP')</a></li><li><a href="external-_Mobile(_incomingConnectionToPortNumberFailed_)_.html">Mobile('incomingConnectionToPortNumberFailed')</a></li><li><a href="external-_Mobile(_killConnections_)_.html">Mobile('killConnections')</a></li><li><a href="external-_Mobile(_networkChanged_)_.html">Mobile('networkChanged')</a></li><li><a href="external-_Mobile(_peerAvailabilityChanged_)_.html">Mobile('peerAvailabilityChanged')</a></li><li><a href="external-_Mobile(_startListeningForAdvertisements_)_.html">Mobile('startListeningForAdvertisements')</a></li><li><a href="external-_Mobile(_startUpdateAdvertisingAndListening_)_.html">Mobile('startUpdateAdvertisingAndListening')</a></li><li><a href="external-_Mobile(_stopAdvertisingAndListening_)_.html">Mobile('stopAdvertisingAndListening')</a></li><li><a href="external-_Mobile(_stopListeningForAdvertisements_)_.html">Mobile('stopListeningForAdvertisements')</a></li></ul><h3>Classes</h3><ul><li><a href="ConnectionTable.html">ConnectionTable</a></li><li><a href="module-TCPServersManager-TCPServersManager.html">TCPServersManager</a></li><li><a href="module-thaliNotificationAction-NotificationAction.html">NotificationAction</a></li><li><a href="module-thaliNotificationBeacons-ParseBeaconsResponse.html">ParseBeaconsResponse</a></li><li><a href="module-thaliNotificationClient-ThaliNotificationClient.html">ThaliNotificationClient</a></li><li><a href="module-thaliNotificationServer-ThaliNotificationServer.html">ThaliNotificationServer</a></li><li><a href="module-thaliPeerAction-PeerAction.html">PeerAction</a></li><li><a href="module-thaliPeerDictionary-NotificationPeerDictionaryEntry.html">NotificationPeerDictionaryEntry</a></li><li><a href="module-thaliPeerDictionary-PeerConnectionInformation.html">PeerConnectionInformation</a></li><li><a href="module-thaliPeerDictionary-PeerDictionary.html">PeerDictionary</a></li><li><a href="module-thaliPeerPoolInterface-ThaliPeerPoolInterface.html">ThaliPeerPoolInterface</a></li><li><a href="module-ThaliWifiInfrastructure-ThaliWifiInfrastructure.html">ThaliWifiInfrastructure</a></li><li><a href="module-WifiBasedNativeMock-MobileCallInstance.html">MobileCallInstance</a></li><li><a href="module-WifiBasedNativeMock-WifiBasedNativeMock.html">WifiBasedNativeMock</a></li></ul><h3>Events</h3><ul><li><a href="module-thaliMobileNativeWrapper.html#~event:discoveryAdvertisingStateUpdateNonTCPEvent">discoveryAdvertisingStateUpdateNonTCPEvent</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:discoveryAdvertisingStateUpdateWifiEvent">discoveryAdvertisingStateUpdateWifiEvent</a></li><li><a href="module-TCPServersManager.html#~event:failedConnection">failedConnection</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:incomingConnectionToPortNumberFailed">incomingConnectionToPortNumberFailed</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:networkChangedNonTCP">networkChangedNonTCP</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:networkChangedWifi">networkChangedWifi</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:nonTCPPeerAvailabilityChangedEvent">nonTCPPeerAvailabilityChangedEvent</a></li><li><a href="module-TCPServersManager.html#~event:routerPortConnectionFailed">routerPortConnectionFailed</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:wifiPeerAvailabilityChanged">wifiPeerAvailabilityChanged</a></li><li><a href="module-thaliMobile.html#.event:event:discoveryAdvertisingStateUpdate">discoveryAdvertisingStateUpdate</a></li><li><a href="module-thaliMobile.html#.event:event:networkChanged">networkChanged</a></li><li><a href="module-thaliMobile.html#.event:event:peerAvailabilityChanged">peerAvailabilityChanged</a></li><li><a href="module-thaliNotificationAction-NotificationAction.html#.event:event:Resolved">Resolved</a></li><li><a href="module-thaliNotificationClient.html#.event:event:peerAdvertisesDataForUs">peerAdvertisesDataForUs</a></li></ul><h3>Global</h3><ul><li><a href="global.html#getPKCS12Content">getPKCS12Content</a></li><li><a href="global.html#getPublicKeyHash">getPublicKeyHash</a></li><li><a href="global.html#stopThaliReplicationManager">stopThaliReplicationManager</a></li><li><a href="global.html#ThaliEmitter">ThaliEmitter</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Mon Jan 18 2016 11:19:31 GMT+0200 (EET) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>