UNPKG

@itwin/presentation-backend

Version:

Backend of iTwin.js Presentation library

147 lines 7.04 kB
/*--------------------------------------------------------------------------------------------- * 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 */ import { BriefcaseDb, IModelHost, IpcHost } from "@itwin/core-backend"; import { Logger } from "@itwin/core-bentley"; import { RpcManager } from "@itwin/core-common"; import { PresentationError, PresentationRpcInterface, PresentationStatus } from "@itwin/presentation-common"; import { PresentationBackendLoggerCategory } from "./BackendLoggerCategory.js"; import { PresentationIpcHandler } from "./PresentationIpcHandler.js"; import { PresentationManager } from "./PresentationManager.js"; import { PresentationRpcImpl } from "./PresentationRpcImpl.js"; import { FactoryBasedTemporaryStorage } from "./TemporaryStorage.js"; import { _presentation_manager_detail } from "./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 */ export 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 = IModelHost.onBeforeShutdown.addListener(() => Presentation.terminate()); this._rpcImpl = new PresentationRpcImpl({ requestTimeout: this._initProps.requestTimeout, }); RpcManager.registerImpl(PresentationRpcInterface, PresentationRpcImpl); RpcManager.supplyImplInstance(PresentationRpcInterface, this._rpcImpl); if (IpcHost.isValid) { this._disposeIpcHandler = PresentationIpcHandler.register(); } this._clientsStorage = new 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) => Logger.logInfo(PresentationBackendLoggerCategory.PresentationManager, `Disposed PresentationManager instance with ID: ${id}. Total instances: ${this._clientsStorage.values.length}.`), /* c8 ignore next */ onDisposedAll: () => Logger.logInfo(PresentationBackendLoggerCategory.PresentationManager, `Disposed all PresentationManager instances.`), }); if (this._initProps.enableSchemasPreload) { this._disposeIModelOpenedListener = 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; } RpcManager.unregisterImpl(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({ ...Presentation._initProps, // @ts-expect-error internal prop id: clientId, }); manager.onUsed.addListener(onManagerUsed); Logger.logInfo(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 PresentationError(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 PresentationError(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[_presentation_manager_detail].getNativePlatform().getImodelAddon(imodel); // eslint-disable-next-line @typescript-eslint/no-floating-promises manager[_presentation_manager_detail].getNativePlatform().forceLoadSchemas(imodelAddon); }; } //# sourceMappingURL=Presentation.js.map