You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

176 lines
4.0 KiB

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];
}
}