@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
265 lines • 12.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.launch = launch;
const api_1 = __importDefault(require("win-ca/api"));
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const Contracts_1 = require("../../Contracts");
const IocRegistrations_1 = require("../../Contracts/IocRegistrations");
const BpmnSeeder_1 = __importDefault(require("../../Tools/BpmnSeeder"));
const Configurator_1 = __importDefault(require("../../Tools/Configurator"));
const DatabaseIntegrityChecker_1 = __importDefault(require("../../Tools/DatabaseAdaptersSequelize/Lib/DatabaseIntegrityChecker"));
const Migrator_1 = __importDefault(require("../../Tools/DatabaseAdaptersSequelize/Migrator"));
const environment = __importStar(require("../../Tools/Environment"));
const ExtensionLoader_1 = __importDefault(require("../../Tools/ExtensionLoader"));
const logger = new processcube_engine_sdk_1.Logger('runtime');
process.title = 'Engine Runtime';
function prepareErrorForExtensionErrors(error) {
const extensionDir = environment.getExtensionDir();
const stack = error.stack || '';
const regex = new RegExp(` \\(${extensionDir}.[^\\/]+`, 'g');
const maybeExtensionErrorMatch = stack.match(regex);
if (maybeExtensionErrorMatch != null) {
const dirOfExtension = maybeExtensionErrorMatch[0].replace(' (', '');
error.stack = error.stack.replace(/.*\n/u, error.message + '\n');
error.category = 'extension';
error.message = `Failure in extension (${dirOfExtension}): ${error.message}`;
}
}
function getLogMessageForError(error) {
if (error instanceof Error || error instanceof processcube_engine_sdk_1.BaseError) {
return '-- An unhandled error was caught! --';
}
if (typeof error == 'string') {
return ('An error was caught that was not an instance of Error.\n' +
'Please wrap thrown errors in the Error class to include a stack trace for later analysis.' +
`\n\nInstead of\n\n throw "${error}";\n\ndo:\n\n throw new Error("${error}");\n\n` +
'If this error originates in a library out of your control, try catching the error and re-throwing it as an Error object to support debugging.');
}
return ('An error was caught that was not an instance of Error.\n' +
'Please wrap thrown errors in the Error class to include a stack trace for later analysis.' +
`\n\nInstead of\n\n throw "My Error Message";\n\ndo:\n\n throw new Error("My Error Message");\n\n` +
'If this error originates in a library out of your control, try catching the error and re-throwing it as an Error object to support debugging.');
}
process.on('exit', (code) => {
console.log(`Runtime Thread is exiting with code: ${code}`);
console.trace();
});
process.on('uncaughtException', (error) => {
prepareErrorForExtensionErrors(error);
if (error.name === 'SequelizeConnectionRefusedError' || error.fatal === true) {
logger.error(getLogMessageForError(error), {
err: error,
});
setTimeout(() => process.exit(1), 2000);
}
else {
logger.warn(getLogMessageForError(error), {
err: error,
});
}
});
process.on('unhandledRejection', (error) => {
prepareErrorForExtensionErrors(error);
if (error.name === 'SequelizeConnectionRefusedError' || error.fatal === true) {
logger.error(getLogMessageForError(error), {
err: error,
});
setTimeout(() => process.exit(1), 2000);
}
else {
logger.warn(getLogMessageForError(error), {
err: error,
});
}
});
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
let startupArgs;
let runtimeRouter;
async function launch(args) {
try {
await initializeEngine(args);
}
catch (error) {
const errorMessagePrefix = error?.category ? `Error in ${error?.category}: ` : '';
logger.error(`${errorMessagePrefix} Runtime Worker error! exiting process...`, { err: error });
setTimeout(() => process.exit(1), 2000);
}
}
async function initializeEngine(args) {
startupArgs = args;
await environment.loadConfig(environment.workerThreadIds.runtime);
const tlsConfig = Configurator_1.default.tls();
const isWin = process.platform === 'win32';
if ((!tlsConfig || tlsConfig.useWindowsTrustStore !== false) && isWin) {
useWindowsTrustStore();
}
const packageJson = environment.readPackageJson();
logger.info(`Starting up server application, version ${packageJson.version}...`);
if (process.env.enableHttp == 'true') {
printHttpInfo();
}
await (0, ExtensionLoader_1.default)(startupArgs.container, environment.getExtensionDir());
await setupDatabase();
const serviceFacade = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.internal.ServiceFacade);
await serviceFacade.init();
await startInternalServices();
await seedBpmns(startupArgs.container);
if (environment.isWindowsAndSQLite()) {
// We do not start the fetchAndLock-Worker on Windows with SQLite, therefore the Runtime must handle ExternalTaskTimers
const externalTaskFetchAndLockService = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.api.services.ExternalTaskFetchAndLockService);
await externalTaskFetchAndLockService.startExternalTaskExpirationTimers();
}
for (const onBeforeResumingCallback of environment.getOnBeforeResumingCallbacks()) {
await onBeforeResumingCallback();
}
if (!startupArgs.resumeProcessInstancesManually) {
await resumeProcessInstances();
}
else {
serviceFacade.registerServiceFunction('resumeProcessInstances', resumeProcessInstances);
}
serviceFacade.registerServiceFunction('getExtensionRoutes', getExtensionRoutes);
// allows the studio to wait for the engine startup
if (typeof process.send === 'function') {
process.send('started');
}
runtimeRouter = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.internal.RuntimeRouter);
await runtimeRouter.init();
for (const onReadyCallback of environment.getOnReadyCallbacks()) {
await onReadyCallback();
}
serviceFacade.emitStartupFinishedMessage();
}
async function setupDatabase() {
// During integrationtests, these are run by the Main Thread, because the database adapters are also initialized there.
if (!process.env.IS_INTEGRATION_TEST_RUN) {
logger.info('Running migrations...');
await (0, Migrator_1.default)();
}
if (process.env.skipDBIntegrityChecks === 'true') {
logger.info('Skipping Database Integrity checks, because --skip-db-integrity-checks was set.');
return;
}
logger.info('Verifying database integrity');
const validationErrors = await (0, DatabaseIntegrityChecker_1.default)();
if (Object.keys(validationErrors).length > 0) {
logger.error('Failure in setup: The database has an incompatible state!', { validationErrors: validationErrors });
// Killing the Process immediately would result in the error not being logged.
await new Promise((resolve) => setTimeout(resolve, 10));
process.exit(1);
}
logger.info('Database has passed integrity checks.');
}
async function seedBpmns(container) {
if (!process.env.seedDir) {
return;
}
try {
const identityService = container.get(IocRegistrations_1.IocRegistrationKeys.internal.IdentityService);
const internalIdentity = identityService.getInternalIdentity();
await (0, BpmnSeeder_1.default)(startupArgs.container, process.env.seedDir, internalIdentity);
}
catch (error) {
const errorCategoryPrefix = error.category ? `Failure in ${error.category}: ` : '';
logger.error(`${errorCategoryPrefix}Seeding failed with error: ${error.message.replace(errorCategoryPrefix, '')}`, {
seedDir: process.env.seedDir,
err: error,
});
}
}
async function startInternalServices() {
try {
const cronjobService = await startupArgs.container.getAsync(IocRegistrations_1.IocRegistrationKeys.core.services.CronjobService);
await cronjobService.start();
logger.trace('CronjobService started.');
const messageEventService = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.core.services.MessageEventService);
await messageEventService.init();
logger.trace('MessageEventService started.');
const signalEventService = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.core.services.SignalEventService);
await signalEventService.init();
logger.trace('SignalService started.');
}
catch (error) {
logger.error('Failed to start the internal services.', {
err: error,
});
await new Promise((resolve) => setTimeout(resolve, 10));
process.exit(1);
}
}
async function resumeProcessInstances() {
const executeProcessService = await startupArgs.container.getAsync(IocRegistrations_1.IocRegistrationKeys.core.services.ExecuteProcessService);
await executeProcessService.findAndResumeInterruptedProcessInstances();
}
function getExtensionRoutes() {
if (!startupArgs.container.isBound(Contracts_1.customHttpRouteTag)) {
return [];
}
const registeredCustomRoutes = startupArgs.container.getAll(Contracts_1.customHttpRouteTag);
const customRoutes = registeredCustomRoutes.map((route) => {
return {
httpRoute: route.httpRoute,
method: route.method,
options: route.options,
};
});
return customRoutes;
}
function printHttpInfo() {
// NOTE:
// The configured port may not necessarily be the actual port used by the Http Server!
// We do not yet make use of port discovery, but as soon as we do, these values may differ!
// The long-term solution would be to get these informations from the Http Server directly,
// but in order to do that, the ProcessEngine must have been started up first.
const httpConfig = environment.getConfig().httpServer;
const configuredAddress = httpConfig.host;
const configuredPort = httpConfig.port;
const addressHasHttpPrefix = configuredAddress.startsWith('http://') || configuredAddress.startsWith('https://');
const showcaseHttpAddress = addressHasHttpPrefix ? configuredAddress : `http://${configuredAddress}`;
logger.info(`Using HTTP endpoint ${showcaseHttpAddress}:${configuredPort}`);
logger.info(`Using websocket endpoint ${showcaseHttpAddress}:${configuredPort}`);
}
function shutdown() {
process.exit(0);
}
function useWindowsTrustStore() {
(0, api_1.default)({ inject: true });
}
//# sourceMappingURL=RuntimeWorker.js.map