UNPKG

diffusion

Version:

Diffusion JavaScript client

324 lines (323 loc) 14.3 kB
"use strict"; /** * @module Session.locks */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InternalSessionLocks = exports.SessionLockImpl = void 0; var internal_session_1 = require("./../client/internal-session"); var Services = require("./../services/session-lock-services"); var logger = require("./../util/logger"); var response_success_1 = require("./../util/response-success"); var session_lock_options_1 = require("../../features/session-lock-options"); var log = logger.create('diffusion.locks.SessionLocks'); /** * Implementation of the {@link SessionLock} interface */ var SessionLockImpl = /** @class */ (function () { /** * Create a new SessionLockImpl instance * * @param acquisition the session lock acquisition * @param ownedSetter the setter that will be initialised to allow * access to the `owned` property * @param unlockSessionLock a callback that will unlock the session lock */ function SessionLockImpl(acquisition, ownedSetter, unlockSessionLock) { var _this = this; /** * Flag indicating if the session lock is owned */ this.owned = true; this.acquisition = acquisition; this.unlockSessionLock = unlockSessionLock; ownedSetter.setOwned = function (newOwned) { _this.owned = newOwned; }; } /** * Convert the SessionLockImpl to a string * * @return the string representation of the session lock */ SessionLockImpl.prototype.toString = function () { return "SessionLock [name=" + this.acquisition.lockName + ", sequence=" + this.acquisition.sequence + ", " + ("scope=" + this.acquisition.scope + ", owned=" + this.owned + "]"); }; /** * @inheritdoc */ SessionLockImpl.prototype.getName = function () { return this.acquisition.lockName; }; /** * @inheritdoc */ SessionLockImpl.prototype.getSequence = function () { return this.acquisition.sequence; }; /** * @inheritdoc */ SessionLockImpl.prototype.isOwned = function () { return this.owned; }; /** * @inheritdoc */ SessionLockImpl.prototype.getScope = function () { return this.acquisition.scope; }; /** * @inheritdoc */ SessionLockImpl.prototype.unlock = function () { if (this.owned) { this.owned = false; return this.unlockSessionLock(this.acquisition); } else { return Promise.resolve(false); } }; return SessionLockImpl; }()); exports.SessionLockImpl = SessionLockImpl; /** * The internal session lock feature */ var InternalSessionLocks = /** @class */ (function () { /** * Create a new InternalSessionLocks implementation * * @param internal the internal session * @param internalFsm the finite state machine of the internal session */ function InternalSessionLocks(internal, internalFsm) { var _this = this; /** * The map of locks indexed by their name */ this.locks = {}; /** * The next request id */ this.nextRequestId = 0; this.serviceLocator = internal.getServiceLocator(); // at the moment we do not implement cancellation of session locks // in the JavaScript api this.ACQUIRE_SESSION_LOCK = this.serviceLocator.obtain(Services.ACQUIRE_SESSION_LOCK); this.RELEASE_SESSION_LOCK = this.serviceLocator.obtain(Services.RELEASE_SESSION_LOCK); var failoverDetectionCid = null; internalFsm.on('change', function (previous, current) { return __awaiter(_this, void 0, void 0, function () { var releaseLock; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: releaseLock = function (v) { v.ownedSetter.setOwned(false); v.onRemoveLock(); }; if (!(current === internal_session_1.InternalSessionState.DISCONNECTED)) return [3 /*break*/, 2]; // this client has detected connection loss Object.values(this.locks) .filter(function (v) { return (v.lock.getScope() === session_lock_options_1.SessionLockScope.UNLOCK_ON_CONNECTION_LOSS); }) .forEach(releaseLock); return [4 /*yield*/, 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(_this.locks).forEach(releaseLock); _this.locks = {}; } })]; case 1: // 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 = _a.sent(); return [3 /*break*/, 3]; case 2: if (current === internal_session_1.InternalSessionState.CONNECTED) { if (failoverDetectionCid !== null) { internal.getConversationSet().respondIfPresent(failoverDetectionCid, null); failoverDetectionCid = null; } } else if (current === internal_session_1.InternalSessionState.CLOSED) { Object.values(this.locks).forEach(releaseLock); this.locks = {}; } _a.label = 3; case 3: return [2 /*return*/]; } }); }); }); } /** * Handle a lock acquisition * * @param lockName the acquired lock name * @param acquisition the acquisition data sent by the server * @return the new session lock */ InternalSessionLocks.prototype.handleAcquisistion = function (lockName, acquisition, handler) { var oldLock = this.locks[lockName]; var sessionLock; // tslint:disable-next-line:strict-type-predicates if (oldLock !== undefined) { if (!oldLock.lock.getSequence().equals(acquisition.sequence)) { // access the session locks owned member through oldLock.ownedSetter.setOwned(false); sessionLock = this.createLock(lockName, acquisition, handler); } else { sessionLock = oldLock.lock; } } else { sessionLock = this.createLock(lockName, acquisition, handler); } return sessionLock; }; InternalSessionLocks.prototype.createLock = function (lockName, acquisition, handler) { var sessionLockOwnedSetter = { setOwned: /* istanbul ignore next */ function () { throw Error('Session lock ownedSetter must be implemented'); } }; var sessionLock = new SessionLockImpl(acquisition, sessionLockOwnedSetter, this.unlock.bind(this)); var lockInfo = handler ? { lock: sessionLock, ownedSetter: { setOwned: function (newOwned) { sessionLockOwnedSetter.setOwned(newOwned); handler.onOwned(newOwned); } }, onRemoveLock: function () { handler.onRemoveLock(); } } : { lock: sessionLock, ownedSetter: sessionLockOwnedSetter, onRemoveLock: function () { } }; this.locks[lockName] = lockInfo; return sessionLock; }; /** * Unlock a session lock identified by the acquisition details * * @param acquisition the lock acquisition details */ InternalSessionLocks.prototype.unlock = function (acquisition) { var _this = this; return new Promise(function (resolve, reject) { _this.RELEASE_SESSION_LOCK.send(acquisition, function (err, response) { if (err) { log.debug('Release session lock failed'); reject(err); } else { // tslint:disable-next-line:strict-type-predicates if (_this.locks[acquisition.lockName] !== undefined && _this.locks[acquisition.lockName].lock.getSequence().toString(10) === acquisition.sequence.toString(10)) { delete _this.locks[acquisition.lockName]; } resolve(response); } }); }); }; /** * Attempt to acquire a lock * * If a lock with the name is already owned by the session, the existing * lock will be returned. Otherwise a request will be sent to the server * * @param lockName the lock name * @param scope the lock scope * @return a {@link Result} that resolves with the acquired session lock */ InternalSessionLocks.prototype.lock = function (lockName, scope, handler) { var _this = this; if (scope === void 0) { scope = session_lock_options_1.SessionLockScope.UNLOCK_ON_SESSION_LOSS; } return new Promise(function (resolve, reject) { // tslint:disable-next-line:strict-type-predicates if (_this.locks[lockName] !== undefined && _this.locks[lockName].lock.isOwned()) { if (handler) { reject(new Error('Cannot register the same lock in different shared sessions')); } else { resolve(_this.locks[lockName].lock); } } else { var requestId = _this.nextRequestId++; _this.ACQUIRE_SESSION_LOCK.send({ lockName: lockName, requestId: requestId, scope: scope }, function (err, acquisition) { if (!response_success_1.responseSuccess(err, acquisition)) { log.debug('Acquire session lock failed'); reject(err); } else { var newLock = _this.handleAcquisistion(lockName, acquisition, handler); resolve(newLock); } }); } }); }; return InternalSessionLocks; }()); exports.InternalSessionLocks = InternalSessionLocks;