pandora
Version:
A powerful and lightweight application manager for Node.js applications powered by TypeScript.
205 lines • 8.68 kB
JavaScript
'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