UNPKG

diffusion

Version:

Diffusion JavaScript client

187 lines (157 loc) 6.7 kB
var _implements = require('util/interface')._implements; var SessionLockScope = require('../../features/session-lock-options').SessionLockScope; var api = require('../../features/session-lock'); var logger = require('util/logger').create('diffusion.locks.SessionLocks'); var Services = require('services/services'); var Emitter = require('events/emitter'); var Result = require('events/result'); var SessionLockImpl = _implements( api.SessionLock, function SessionLockImpl(acquisition, locks, ownedSetter, RELEASE_SESSION_LOCK) { var owned = true; ownedSetter.setOwned = function(newOwned) { owned = newOwned; }; this.toString = function () { return "SessionLock [name="+ acquisition.lockName + ", sequence=" + acquisition.sequence + ", scope=" + acquisition.scope + ", owned=" + owned + "]"; }; this.getName = function() { return acquisition.lockName; }; this.getSequence = function() { return acquisition.sequence; }; this.isOwned = function() { return owned; }; this.getScope = function() { return acquisition.scope; }; this.unlock = function() { var emitter = new Emitter(); var result = new Result(emitter); if (owned) { owned = false; RELEASE_SESSION_LOCK.send( acquisition, function(err, result) { if (err) { logger.debug('Release session lock failed'); emitter.error(err); } else { if (locks[acquisition.name] !== undefined && locks[acquisition.name].lock === this) { delete locks[acquisition.name]; } emitter.emit('complete', result); } } ); } else { emitter.emit('complete', false); } return result; }; } ); var InternalSessionLocks = function(internal, internalFsm) { var serviceLocator = internal.getServiceLocator(); // At the moment we do not implement cancellation of session locks // in the JavaScript api var ACQUIRE_SESSION_LOCK = serviceLocator.obtain(Services.ACQUIRE_SESSION_LOCK); var RELEASE_SESSION_LOCK = serviceLocator.obtain(Services.RELEASE_SESSION_LOCK); var locks = {}; var nextRequestId = 0; internalFsm.on('change', function (previous, current) { var failoverDetectionCid = null; var releaseLock = function (v) { v.ownedSetter.setOwned(false); }; if (current === 'disconnected') { // This client has detected connection loss. Object .values(locks) .filter(function (v) { return v.lock.getScope() === SessionLockScope.UNLOCK_ON_CONNECTION_LOSS; }) .forEach(releaseLock); // There could be pending duplicate acquisitions // that will arrive after reconnection. We leave // lock in locks so we can filter these when they // arrive. // Abuse a conversation set to detect failover and release the locks. // Relies on the RECONNECTED_WITH_MESSAGE_LOSS handling to discard all current conversations. failoverDetectionCid = internal.getConversationSet().newConversation({ onOpen : function() { // No-op }, onResponse : function() { // Called on successful connection. Close this conversation. return true; }, onDiscard : function() { // Called on failover. We've lost all of the locks. Object.values(locks).forEach(releaseLock); locks = {}; } }); } else if (current === 'connected') { if (failoverDetectionCid !== null) { internal.getConversationSet().respondIfPresent(failoverDetectionCid, null); failoverDetectionCid = null; } } else if (current === 'closed') { Object.values(locks).forEach(releaseLock); locks = {}; } }); function handleAcquisistion(lockName, acquisition) { var oldLock = locks[lockName]; var sessionLock; var sessionLockOwnedSetter = { setOwned: function() { throw Error("Session lock ownedSetter must be implemented"); } }; if (oldLock !== undefined) { if (!oldLock.lock.getSequence().equals(acquisition.sequence)) { // Access the session locks owned member through oldLock.ownedSetter.setOwned(false); sessionLock = new SessionLockImpl(acquisition, locks, sessionLockOwnedSetter, RELEASE_SESSION_LOCK); locks[lockName] = {lock: sessionLock, ownedSetter: sessionLockOwnedSetter}; } else { sessionLock = oldLock.lock; } } else { sessionLock = new SessionLockImpl(acquisition, locks, sessionLockOwnedSetter, RELEASE_SESSION_LOCK); locks[lockName] = {lock: sessionLock, ownedSetter: sessionLockOwnedSetter}; } return sessionLock; } this.lock = function(lockName, scope) { var emitter = new Emitter(); var result = new Result(emitter); scope = scope === undefined ? SessionLockScope.UNLOCK_ON_SESSION_LOSS : scope; if (locks[lockName]!==undefined && locks[lockName].lock.isOwned()) { emitter.emit('complete', locks[lockName].lock); } else { var requestId = nextRequestId++; ACQUIRE_SESSION_LOCK.send({ lockName: lockName, requestId: requestId, scope: scope }, function(err, acquisition) { if (err) { logger.debug('Acquire session lock failed'); emitter.error(err); } else { var newLock = handleAcquisistion(lockName, acquisition); emitter.emit('complete', newLock); } }); } return result; }; }; module.exports.SessionLock = SessionLockImpl; module.exports.InternalSessionLocks = InternalSessionLocks;