diffusion
Version:
Diffusion JavaScript client
187 lines (157 loc) • 6.7 kB
JavaScript
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;