UNPKG

tgrid

Version:

Grid Computing Framework for TypeScript

611 lines 27.2 kB
"use strict"; 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 (g && (g = 0, op[0] && (_ = 0)), _) 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 }; } }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Communicator = void 0; var tstl_1 = require("tstl"); var Driver_1 = require("../typings/Driver"); var serializeError_1 = require("../utils/internal/serializeError"); /** * The basic communicator. * * The `Communicator` is an abstract class taking full charge of network communication. * Protocolized communicators like {@link WebSocketConnector} are realized by extending this * `Communicator` class. * * You want to make your own communicator using special protocol, extends this `Communicator` * class. After the extending, implement your special communicator by overriding those methods. * * - {@link inspectReady} * - {@link replyData} * - {@link sendData} * * @template Provider Type of features provided for remote system. * @template Remote Type of features supported by remote system, used for {@link getDriver} function. * @author Jeongho Nam - https://github.com/samchon */ var Communicator = /** @class */ (function () { /* ---------------------------------------------------------------- CONSTRUCTORS ---------------------------------------------------------------- */ /** * Initializer Constructor. * * @param provider An object providing features for remote system. */ function Communicator(provider) { var _this = this; // PROVIDER & DRIVER this.provider_ = provider; this.driver_ = new Proxy(new Driver_1.Driver(), { get: function (_a, name) { if (name === "then") return null; else return _this._Proxy_func(name); }, }); // OTHER MEMBERS this.promises_ = new tstl_1.HashMap(); this.join_cv_ = new tstl_1.ConditionVariable(); this.event_listeners_ = new tstl_1.HashMap(); } /** * Add invoke event listener. * * Add an event listener for the invoke event. The event listener would be called * when some invoke event has been occured; sending, receiving, completing, or returning. * * If you change the requesting parameters or returning value in the event listener, * it would affect to the RPC (Remote Procedure Call) communication. Therefore, you have * to be careful when modifying the remote function calling. * * Of course, you can utilize the event listener just for monitoring the RPC events. * * @param type Type of the event * @param listener The listener function to enroll */ Communicator.prototype.on = function (type, listener) { this.event_listeners_ .take(type, function () { return new tstl_1.HashSet(); }) .insert(listener); }; /** * Erase invoke event listener. * * Erase an event listener from the invoke event. The event listener would not be * called anymore when the specific invoke event has been occured. * * @param type Type of the event * @param listener The listener function to erase */ Communicator.prototype.off = function (type, listener) { var it = this.event_listeners_.find(type); if (it.equals(this.event_listeners_.end()) === false) it.second.erase(listener); if (it.second.empty()) this.event_listeners_.erase(it); }; /** * Destroy the communicator. * * A destroy function must be called when the network communication has been closed. * It would destroy all function calls in the remote system (by `Driver<Controller>`), * which are not returned yet. * * The *error* instance would be thrown to those function calls. If the disconnection is * abnormal, then write the detailed reason why into the *error* instance. * * @param error An error instance to be thrown to the unreturned functions. */ Communicator.prototype.destructor = function (error) { return __awaiter(this, void 0, void 0, function () { var rejectError, _a, _b, entry, reject; var e_1, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: rejectError = error ? error : new Error("Connection has been closed."); try { for (_a = __values(this.promises_), _b = _a.next(); !_b.done; _b = _a.next()) { entry = _b.value; reject = entry.second.reject; reject(rejectError); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_b && !_b.done && (_c = _a.return)) _c.call(_a); } finally { if (e_1) throw e_1.error; } } // CLEAR PROMISES this.promises_.clear(); // RESOLVE JOINERS return [4 /*yield*/, this.join_cv_.notify_all()]; case 1: // RESOLVE JOINERS _d.sent(); return [2 /*return*/]; } }); }); }; /** * @hidden */ Communicator.prototype._Proxy_func = function (name) { var _this = this; var func = function () { var params = []; for (var _i = 0; _i < arguments.length; _i++) { params[_i] = arguments[_i]; } return _this._Call_function.apply(_this, __spreadArray([name], __read(params), false)); }; return new Proxy(func, { get: function (_a, newName) { if (newName === "bind") return function (thisArg) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } return func.bind.apply(func, __spreadArray([thisArg], __read(args), false)); }; else if (newName === "call") return function (thisArg) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } return func.call.apply(func, __spreadArray([thisArg], __read(args), false)); }; else if (newName === "apply") return function (thisArg, args) { return func.apply(thisArg, args); }; return _this._Proxy_func("".concat(name, ".").concat(newName)); }, }); }; /** * @hidden */ Communicator.prototype._Call_function = function (name) { var _this = this; var params = []; for (var _i = 1; _i < arguments.length; _i++) { params[_i - 1] = arguments[_i]; } return new Promise(function (resolve, reject) { var e_2, _a; // READY TO SEND ? var error = _this.inspectReady("Communicator._Call_fuction"); if (error) { reject(error); return; } // CONSTRUCT INVOKE MESSAGE var invoke = { uid: ++Communicator.SEQUENCE, listener: name, parameters: params.map(function (p) { return ({ type: typeof p, value: p, }); }), }; // CALL EVENT LISTENERS var eventSetIterator = _this.event_listeners_.find("send"); if (eventSetIterator.equals(_this.event_listeners_.end()) === false) { var event_1 = { type: "send", time: new Date(), function: invoke, }; try { for (var _b = __values(eventSetIterator.second), _c = _b.next(); !_c.done; _c = _b.next()) { var listener = _c.value; try { listener(event_1); } catch (_d) { } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } } // DO SEND WITH PROMISE _this.promises_.emplace(invoke.uid, { function: invoke, time: new Date(), resolve: resolve, reject: reject, }); Promise.resolve(_this.sendData(invoke)).catch(function (error) { _this.promises_.erase(invoke.uid); reject(error); }); }); }; /* ---------------------------------------------------------------- ACCESSORS ---------------------------------------------------------------- */ /** * Set `Provider` * * @param obj An object would be provided for remote system. */ Communicator.prototype.setProvider = function (obj) { this.provider_ = obj; }; /** * Get current `Provider`. * * Get an object providing features (functions & objects) for remote system. The remote * system would call the features (`Provider`) by using its `Driver<Controller>`. * * @return Current `Provider` object */ Communicator.prototype.getProvider = function () { return this.provider_; }; /** * Get Driver for RFC (Remote Function Call). * * The `Controller` is an interface who defines provided functions from the remote * system. The `Driver` is an object who makes to call remote functions, defined in * the `Controller` and provided by `Provider` in the remote system, possible. * * In other words, calling a functions in the `Driver<Controller>`, it means to call * a matched function in the remote system's `Provider` object. * * - `Controller`: Definition only * - `Driver`: Remote Function Call * * @template Controller An interface for provided features (functions & objects) from the remote system (`Provider`). * @template UseParametric Whether to convert type of function parameters to be compatible with their primitive. * @return A Driver for the RFC. */ Communicator.prototype.getDriver = function () { return this.driver_; }; Communicator.prototype.join = function (param) { return __awaiter(this, void 0, void 0, function () { var error; return __generator(this, function (_a) { switch (_a.label) { case 0: error = this.inspectReady("".concat(this.constructor.name, ".join")); if (error) throw error; if (!(param === undefined)) return [3 /*break*/, 2]; return [4 /*yield*/, this.join_cv_.wait()]; case 1: _a.sent(); return [3 /*break*/, 6]; case 2: if (!(param instanceof Date)) return [3 /*break*/, 4]; return [4 /*yield*/, this.join_cv_.wait_until(param)]; case 3: return [2 /*return*/, _a.sent()]; case 4: return [4 /*yield*/, this.join_cv_.wait_for(param)]; case 5: return [2 /*return*/, _a.sent()]; case 6: return [2 /*return*/]; } }); }); }; /* ================================================================ COMMUNICATORS - REPLIER - SENDER =================================================================== REPLIER ---------------------------------------------------------------- */ /** * Data Reply Function. * * A function should be called when data has come from the remote system. * * When you receive a message from the remote system, then parse the message with your * special protocol and covert it to be an *Invoke* object. After the conversion, call * this method. * * @param invoke Structured data converted by your special protocol. */ Communicator.prototype.replyData = function (invoke) { if (invoke.listener) this._Handle_function(invoke).catch(function () { }); else this._Handle_complete(invoke); }; /** * @hidden */ Communicator.prototype._Handle_function = function (invoke) { return __awaiter(this, void 0, void 0, function () { var uid, time, func, thisArg, routes, routes_1, routes_1_1, name_1, eventSetIterator, event_2, _a, _b, closure, parameters, result, exp_1; var e_3, _c, e_4, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: uid = invoke.uid; time = new Date(); _e.label = 1; case 1: _e.trys.push([1, 4, , 6]); //---- // FIND FUNCTION //---- if (this.provider_ === undefined) // PROVIDER MUST BE throw new Error("Error on Communicator._Handle_function(): the provider is not specified yet."); else if (this.provider_ === null) throw new Error("Error on Communicator._Handle_function(): the provider would not be."); func = this.provider_; thisArg = undefined; routes = invoke.listener.split("."); try { for (routes_1 = __values(routes), routes_1_1 = routes_1.next(); !routes_1_1.done; routes_1_1 = routes_1.next()) { name_1 = routes_1_1.value; thisArg = func; func = thisArg[name_1]; // SECURITY-ERRORS if (name_1[0] === "_") throw new Error("Error on Communicator._Handle_function(): RFC does not allow access to a member starting with the underscore: Provider.".concat(invoke.listener, "()")); else if (name_1[name_1.length - 1] === "_") throw new Error("Error on Communicator._Handle_function(): RFC does not allow access to a member ending with the underscore: Provider.".concat(invoke.listener, "().")); else if (name_1 === "toString" && func === Function.toString) throw new Error("Error on Communicator._Handle_function(): RFC on Function.toString() is not allowed: Provider.".concat(invoke.listener, "().")); else if (name_1 === "constructor" || name_1 === "prototype") throw new Error("Error on Communicator._Handle_function(): RFC does not allow access to ".concat(name_1, ": Provider.").concat(invoke.listener, "().")); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (routes_1_1 && !routes_1_1.done && (_c = routes_1.return)) _c.call(routes_1); } finally { if (e_3) throw e_3.error; } } func = func.bind(thisArg); eventSetIterator = this.event_listeners_.find("receive"); if (eventSetIterator.equals(this.event_listeners_.end()) === false) { event_2 = { type: "receive", time: time, function: invoke, }; try { for (_a = __values(eventSetIterator.second), _b = _a.next(); !_b.done; _b = _a.next()) { closure = _b.value; try { closure(event_2); } catch (_f) { } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_b && !_b.done && (_d = _a.return)) _d.call(_a); } finally { if (e_4) throw e_4.error; } } } parameters = invoke.parameters.map(function (p) { return p.value; }); return [4 /*yield*/, func.apply(void 0, __spreadArray([], __read(parameters), false))]; case 2: result = _e.sent(); return [4 /*yield*/, this._Send_return({ invoke: invoke, time: time, return: { uid: uid, success: true, value: result, }, })]; case 3: _e.sent(); return [3 /*break*/, 6]; case 4: exp_1 = _e.sent(); return [4 /*yield*/, this._Send_return({ invoke: invoke, time: time, return: { uid: uid, success: false, value: exp_1, }, })]; case 5: _e.sent(); return [3 /*break*/, 6]; case 6: return [2 /*return*/]; } }); }); }; /** * @hidden */ Communicator.prototype._Handle_complete = function (invoke) { var e_5, _a; // FIND TARGET FUNCTION CALL var it = this.promises_.find(invoke.uid); if (it.equals(this.promises_.end())) return; // CALL EVENT LISTENERS var eventSetIterator = this.event_listeners_.find("complete"); if (eventSetIterator.equals(this.event_listeners_.end()) === false) { var event_3 = { type: "complete", function: it.second.function, return: invoke, requested_at: it.second.time, completed_at: new Date(), }; try { for (var _b = __values(eventSetIterator.second), _c = _b.next(); !_c.done; _c = _b.next()) { var closure = _c.value; try { closure(event_3); } catch (_d) { } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_5) throw e_5.error; } } } // RETURNS var func = invoke.success ? it.second.resolve : it.second.reject; this.promises_.erase(it); func(invoke.value); }; /** * @hidden */ Communicator.prototype._Send_return = function (props) { return __awaiter(this, void 0, void 0, function () { var eventSet, event_4, _a, _b, closure, _c; var e_6, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: eventSet = this.event_listeners_.find("return"); if (eventSet.equals(this.event_listeners_.end()) === false) { event_4 = { type: "return", function: props.invoke, return: props.return, requested_at: props.time, completed_at: new Date(), }; try { for (_a = __values(eventSet.second), _b = _a.next(); !_b.done; _b = _a.next()) { closure = _b.value; try { closure(event_4); } catch (_f) { } } } catch (e_6_1) { e_6 = { error: e_6_1 }; } finally { try { if (_b && !_b.done && (_d = _a.return)) _d.call(_a); } finally { if (e_6) throw e_6.error; } } } // SPECIAL LOGIC FOR ERROR -> FOR CLEAR JSON ENCODING if (props.return.success === false && props.return.value instanceof Error) props.return.value = (0, serializeError_1.serializeError)(props.return.value); _e.label = 1; case 1: _e.trys.push([1, 3, , 4]); return [4 /*yield*/, this.sendData(props.return)]; case 2: _e.sent(); return [3 /*break*/, 4]; case 3: _c = _e.sent(); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; /** * @hidden */ Communicator.SEQUENCE = 0; return Communicator; }()); exports.Communicator = Communicator; //# sourceMappingURL=Communicator.js.map