@itwin/presentation-backend
Version:
Backend of iTwin.js Presentation library
151 lines • 7.66 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Core
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Presentation = void 0;
const core_backend_1 = require("@itwin/core-backend");
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const presentation_common_1 = require("@itwin/presentation-common");
const BackendLoggerCategory_js_1 = require("./BackendLoggerCategory.js");
const PresentationIpcHandler_js_1 = require("./PresentationIpcHandler.js");
const PresentationManager_js_1 = require("./PresentationManager.js");
const PresentationRpcImpl_js_1 = require("./PresentationRpcImpl.js");
const TemporaryStorage_js_1 = require("./TemporaryStorage.js");
const InternalSymbols_js_1 = require("./InternalSymbols.js");
/**
* Static class used to statically set up Presentation library for the backend.
* Basically what it does is:
* - Register a RPC implementation
* - Create a singleton [[PresentationManager]] instance
* - Subscribe for [IModelHost.onBeforeShutdown]($core-backend) event and terminate
* the presentation manager when that happens.
*
* @public
*/
class Presentation {
static _initProps;
static _clientsStorage;
static _disposeIpcHandler;
static _shutdownListener;
static _disposeIModelOpenedListener;
static _rpcImpl;
/* c8 ignore next */
constructor() { }
/** Properties used to initialize the presentation framework */
static get initProps() {
return this._initProps;
}
/**
* Initializes Presentation library for the backend.
*
* See [Setting up iTwin.js Presentation library documentation page]($docs/presentation/setup/index.md#backend) for an example.
*
* **Important:** The method should be called after a call to [IModelHost.startup]($core-backend)
*
* @param props Optional properties for [[PresentationManager]]
*/
static initialize(props) {
this._initProps = props || {};
this._shutdownListener = core_backend_1.IModelHost.onBeforeShutdown.addListener(() => Presentation.terminate());
this._rpcImpl = new PresentationRpcImpl_js_1.PresentationRpcImpl({
requestTimeout: this._initProps.requestTimeout,
});
core_common_1.RpcManager.registerImpl(presentation_common_1.PresentationRpcInterface, PresentationRpcImpl_js_1.PresentationRpcImpl);
core_common_1.RpcManager.supplyImplInstance(presentation_common_1.PresentationRpcInterface, this._rpcImpl);
if (core_backend_1.IpcHost.isValid) {
this._disposeIpcHandler = PresentationIpcHandler_js_1.PresentationIpcHandler.register();
}
this._clientsStorage = new TemporaryStorage_js_1.FactoryBasedTemporaryStorage({
factory: this.createClientManager.bind(this),
cleanupHandler: (_id, storeItem) => this.disposeClientManager(_id, storeItem),
// cleanup unused managers every minute
cleanupInterval: 60 * 1000,
// by default, manager is disposed after 1 hour of being unused
unusedValueLifetime: this._initProps.unusedClientLifetime ?? 60 * 60 * 1000,
// add some logging
/* c8 ignore next 5 */
onDisposedSingle: (id) => core_bentley_1.Logger.logInfo(BackendLoggerCategory_js_1.PresentationBackendLoggerCategory.PresentationManager, `Disposed PresentationManager instance with ID: ${id}. Total instances: ${this._clientsStorage.values.length}.`),
/* c8 ignore next */
onDisposedAll: () => core_bentley_1.Logger.logInfo(BackendLoggerCategory_js_1.PresentationBackendLoggerCategory.PresentationManager, `Disposed all PresentationManager instances.`),
});
if (this._initProps.enableSchemasPreload) {
this._disposeIModelOpenedListener = core_backend_1.BriefcaseDb.onOpened.addListener(this.onIModelOpened);
}
}
/**
* Terminates Presentation. Consumers don't need to call this as it's automatically
* called on [IModelHost.onBeforeShutdown]($core-backend) event.
*/
static terminate() {
if (this._clientsStorage) {
this._clientsStorage[Symbol.dispose]();
this._clientsStorage = undefined;
}
if (this._disposeIModelOpenedListener) {
this._disposeIModelOpenedListener();
this._disposeIModelOpenedListener = undefined;
}
if (this._shutdownListener) {
this._shutdownListener();
this._shutdownListener = undefined;
}
core_common_1.RpcManager.unregisterImpl(presentation_common_1.PresentationRpcInterface);
if (this._rpcImpl) {
this._rpcImpl[Symbol.dispose]();
this._rpcImpl = undefined;
}
if (this._disposeIpcHandler) {
this._disposeIpcHandler();
}
this._initProps = undefined;
}
static createClientManager(clientId, onManagerUsed) {
const manager = Presentation._initProps && Presentation._initProps.clientManagerFactory
? Presentation._initProps.clientManagerFactory(clientId, Presentation._initProps)
: new PresentationManager_js_1.PresentationManager({
...Presentation._initProps,
// @ts-expect-error internal prop
id: clientId,
});
manager.onUsed.addListener(onManagerUsed);
core_bentley_1.Logger.logInfo(BackendLoggerCategory_js_1.PresentationBackendLoggerCategory.PresentationManager, `Created a PresentationManager instance with ID: ${clientId}. Total instances: ${this._clientsStorage.values.length}.`);
return { manager };
}
static disposeClientManager(_id, storeItem) {
storeItem.manager[Symbol.dispose]();
}
/**
* Get an instance of [[PresentationManager]] for specific client
* @param clientId ID of the client requesting presentation data. If no
* ID is provided, the default [[PresentationManager]] is returned.
*/
static getManager(clientId) {
if (this._clientsStorage) {
return this._clientsStorage.getValue(clientId || "").manager;
}
throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.NotInitialized, "Presentation must be first initialized by calling Presentation.initialize");
}
/**
* Get the time in milliseconds that backend should respond in .
*/
static getRequestTimeout() {
if (this._rpcImpl === undefined) {
throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.NotInitialized, "Presentation must be first initialized by calling Presentation.initialize");
}
return this._rpcImpl.requestTimeout;
}
static onIModelOpened = (imodel) => {
const manager = this.getManager();
const imodelAddon = manager[InternalSymbols_js_1._presentation_manager_detail].getNativePlatform().getImodelAddon(imodel);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
manager[InternalSymbols_js_1._presentation_manager_detail].getNativePlatform().forceLoadSchemas(imodelAddon);
};
}
exports.Presentation = Presentation;
//# sourceMappingURL=Presentation.js.map