UNPKG

pandora

Version:

A powerful and lightweight application manager for Node.js applications powered by TypeScript.

205 lines 8.68 kB
'use strict'; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const assert = require("assert"); const ServiceCore_1 = require("./ServiceCore"); const debug = require('debug')('pandora:ServiceReconciler'); /** * Class ServiceReconciler */ class ServiceReconciler { constructor(processRepresentation, context, workModeByForce) { this.services = new Map; this.state = 'notBoot'; this.workModeByForce = workModeByForce; this.processRepresentation = processRepresentation; this.context = context; } /** * Receive a service representation * @param {ServiceRepresentation} serviceRepresentation */ receiveServiceRepresentation(serviceRepresentation) { debug('receiveServiceRepresentation() %j', serviceRepresentation); const id = serviceRepresentation.serviceName; if (this.services.has(id)) { return; } const ref = { serviceRepresentation: serviceRepresentation, state: 'noinstance' }; this.services.set(serviceRepresentation.serviceName, ref); } /** * Get ordered service id set, order by service weight * @param {"asc" | "desc"} order * @return {Array} */ getOrderedServiceIdSet(order) { const ret = []; for (const id of this.services.keys()) { ret.push({ id, weight: this.getWeight(id) }); } return ret.sort((a, b) => { if (order === 'asc') { return a.weight - b.weight; } else { return b.weight - a.weight; } }); } /** * Get service's weight by service's ID (name) * @param id * @param {string[]} chain * @return {any} */ getWeight(id, chain) { chain = Array.from(chain || []); assert(-1 === chain.indexOf(id), `Service name: ${id} in a cyclic dependency chain: ${chain.join(' -> ')} -> ${id}`); if (chain.length > 1 && id === 'all') { throw new Error(`Reserved service name 'all' not allowed to contains within a dependency chain: ${chain.join(' -> ')} -> ${id}`); } if (id === 'all') { return Infinity; } chain.push(id); assert(this.services.has(id), `Could not found service id: ${id}`); const ref = this.services.get(id); const { serviceRepresentation } = ref; if (!serviceRepresentation.dependencies || !serviceRepresentation.dependencies.length) { return 1; } else { const nextLevelWeights = []; for (const nextId of serviceRepresentation.dependencies) { nextLevelWeights.push(this.getWeight(nextId, chain)); } return Math.max.apply(Math, nextLevelWeights) + 1; } } /** * Instantiate all the services */ instantiate() { for (const { id } of this.getOrderedServiceIdSet('asc')) { assert(this.services.has(id), `Could not found service id: ${id}`); const ref = this.services.get(id); const { state, serviceRepresentation } = ref; debug('instantiateOne() request %s', id); if (state === 'noinstance') { debug('instantiateOne() instantiate %s', id); const deps = serviceRepresentation.dependencies; const depInstances = {}; if (deps) { for (let depId of deps) { if (depId === 'all') { continue; } depInstances[depId] = this.services.get(depId).serviceCoreInstance; } } const serviceEntry = serviceRepresentation.serviceEntry.getLazyClass ? serviceRepresentation.serviceEntry.getLazyClass() : serviceRepresentation.serviceEntry; serviceRepresentation.config = serviceRepresentation.configResolver ? serviceRepresentation.configResolver(this.context.workerContextAccessor, serviceRepresentation.config) : serviceRepresentation.config; const serviceCoreInstance = new ServiceCore_1.ServiceCore({ context: this.context.workerContextAccessor, representation: serviceRepresentation, depInstances: depInstances }, serviceEntry); ref.serviceCoreInstance = serviceCoreInstance; ref.state = 'instanced'; debug('instantiateOne() instanced %s', id); } } } /** * Start all the services * @return {Promise<void>} */ start() { return __awaiter(this, void 0, void 0, function* () { debug('start()'); this.instantiate(); // Maybe start mutilate times, only set state at first time if (this.state !== 'booted') { this.state = 'booting'; } for (const { id } of this.getOrderedServiceIdSet('asc')) { assert(this.services.has(id), `Could not found service id: ${id}`); const ref = this.services.get(id); debug('startOne() instanced request %s', id); assert(ref.state !== 'noinstance', 'instantiate first before start ' + id); if (ref.state === 'instanced') { debug('startOne() start %s', id); ref.state = 'booting'; const serviceCore = ref.serviceCoreInstance; // midwayClassicPluginService 中需要在启动过程中,通过 getService 获得到启动过程中的自己,提早实例产生时机 ref.serviceInstance = serviceCore.instantiate(); yield serviceCore.start(); ref.state = 'booted'; debug('startOne() booted %s', id); } } debug('start() booted'); this.state = 'booted'; }); } stop() { return __awaiter(this, void 0, void 0, function* () { debug('stop()'); if (this.state === 'notBoot') { return; } for (const { id } of this.getOrderedServiceIdSet('desc')) { assert(this.services.has(id), `Could not found service id: ${id}`); const ref = this.services.get(id); debug('stopOne() instanced request %s', id); assert(ref.state !== 'noinstance', 'instantiate first before stop ' + id); if (ref.state === 'booted') { debug('stopOne() start %s', id); ref.state = 'stopping'; const serviceCore = ref.serviceCoreInstance; yield serviceCore.stop(); ref.serviceInstance = serviceCore.getService(); ref.state = 'instanced'; debug('startOne() stopped %s', id); } } debug('stop() stopped'); this.state = 'notBoot'; }); } get(id) { assert(this.services.has(id), `Could not found service id: ${id}`); const ref = this.services.get(id); assert(ref.serviceInstance, `Service id: ${id} have not instance yet`); return ref.serviceInstance; } getServiceClass(serviceName) { const ref = this.services.get(serviceName); if (ref) { const serviceRepresentation = ref.serviceRepresentation; const serviceEntry = serviceRepresentation.serviceEntry.getLazyClass ? serviceRepresentation.serviceEntry.getLazyClass() : serviceRepresentation.serviceEntry; return serviceEntry; } return null; } getState() { return this.state; } } exports.ServiceReconciler = ServiceReconciler; //# sourceMappingURL=ServiceReconciler.js.map