import { InternalService } from "./InternalService";
|
|
import assert from "assert";
|
|
|
|
/**
|
|
* Application InternalService map
|
|
*/
|
|
interface InternalServiceMap {
|
|
[name: string]: InternalService
|
|
}
|
|
|
|
/**
|
|
* The InternalService constructor type
|
|
*/
|
|
type InternalServiceConstructor<T extends Microservice = Microservice> = new (app: T) => InternalService<T>;
|
|
|
|
/**
|
|
* Microservice states
|
|
*/
|
|
export enum MicroserviceState {
|
|
Idling,
|
|
Booting,
|
|
Starting,
|
|
Running,
|
|
Quitting,
|
|
Finished
|
|
}
|
|
|
|
/**
|
|
* The main application class
|
|
*/
|
|
export class Microservice
|
|
{
|
|
/**
|
|
* Maintain a static reference to the application instance
|
|
*/
|
|
private static __instance: Microservice;
|
|
|
|
/**
|
|
* A handler function to quit the microservice application
|
|
*/
|
|
private __quitHandler!: (value: number | PromiseLike<number>) => void;
|
|
|
|
/**
|
|
* All available services
|
|
*/
|
|
protected services: InternalServiceMap = {};
|
|
|
|
/**
|
|
* The current state of the microservice
|
|
*/
|
|
protected state: MicroserviceState;
|
|
|
|
/**
|
|
* Return the current application instance
|
|
*/
|
|
public static instance() {
|
|
return this.__instance;
|
|
}
|
|
|
|
/**
|
|
* Create a new application instance
|
|
*/
|
|
public constructor() {
|
|
Microservice.__instance = this;
|
|
this.state = MicroserviceState.Idling;
|
|
}
|
|
|
|
// Overridable --------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Invoked when the application has finished booting
|
|
*/
|
|
protected onStart(): void|Promise<void> {}
|
|
|
|
// Application Management ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Boot the application and all of the services
|
|
*/
|
|
protected async boot() {
|
|
let InternalServices = Object.values(this.services);
|
|
return Promise.all(InternalServices.map(InternalService => InternalService.boot()));
|
|
}
|
|
|
|
/**
|
|
* Shutdown the application
|
|
*/
|
|
protected shutdown() {
|
|
let InternalServices = Object.values(this.services);
|
|
return Promise.all(InternalServices.map(InternalService => InternalService.shutdown()));
|
|
}
|
|
|
|
/**
|
|
* Start the application
|
|
*/
|
|
public async exec() {
|
|
// Exit if not in an idling state
|
|
if (this.state !== MicroserviceState.Idling) {
|
|
return -1;
|
|
}
|
|
|
|
try {
|
|
// Boot the microservice
|
|
this.state = MicroserviceState.Booting;
|
|
await this.boot();
|
|
|
|
// Start the microservice
|
|
this.state = MicroserviceState.Starting
|
|
await this.onStart();
|
|
for (let InternalService of Object.values(this.services)) {
|
|
InternalService.start();
|
|
}
|
|
} catch(e) {
|
|
console.error("Failed to start the microservice:", e);
|
|
return 1;
|
|
}
|
|
|
|
// Run the microservice
|
|
this.state = MicroserviceState.Running;
|
|
process.on("SIGINT", this.quit.bind(this));
|
|
let exitCode = await new Promise<number>((resolve) => this.__quitHandler = resolve);
|
|
|
|
// Shutdown the microservice
|
|
await this.shutdown().catch(() => {
|
|
console.log("Error ocurred during shutdown...");
|
|
exitCode = 1;
|
|
});
|
|
|
|
// Return the exit code
|
|
return exitCode;
|
|
}
|
|
|
|
/**
|
|
* Quit the application
|
|
*/
|
|
public async quit(code: number = 0) {
|
|
if (this.state !== MicroserviceState.Running) {
|
|
return;
|
|
}
|
|
this.__quitHandler(code);
|
|
}
|
|
|
|
// InternalService Management --------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Install InternalServices into the application
|
|
*/
|
|
public installServices<T extends Microservice>(this: T, InternalServices: InternalServiceConstructor<T>[]) {
|
|
for (let InternalServiceClass of InternalServices) {
|
|
this.installService(InternalServiceClass);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Install a InternalService into the application
|
|
*/
|
|
public installService<T extends Microservice>(this: T, InternalServiceClass: InternalServiceConstructor<T>) {
|
|
let InternalService = new InternalServiceClass(this);
|
|
this.services[InternalService.NAME] = InternalService;
|
|
}
|
|
|
|
/**
|
|
* Get all available services
|
|
*/
|
|
public serviceList() {
|
|
return Object.keys(this.services);
|
|
}
|
|
|
|
/**
|
|
* Get an application services instance
|
|
*/
|
|
public service<T extends InternalService<Microservice>>(InternalServiceName: string) {
|
|
assert(InternalServiceName in this.services);
|
|
return <T>this.services[InternalServiceName];
|
|
}
|
|
}
|