UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

432 lines (391 loc) 14.3 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([ './LRUPersistentCache', './CacheManagerNOP', 'sap/ui/Device', "sap/base/config", "sap/base/Log", "sap/ui/performance/Measurement", 'sap/ui/performance/trace/Interaction' ], function(LRUPersistentCache, CacheManagerNOP, Device, BaseConfig, Log, Measurement, Interaction) { "use strict"; var oWritableConfig = BaseConfig.getWritableInstance(); function isUI5CacheOn() { return oWritableConfig.get({ name: "sapUiXxCacheUse", type: BaseConfig.Type.Boolean, defaultValue: true, external: true }); } function setUI5CacheOn(bActive) { oWritableConfig.set("sapUiXxCacheUse", bActive); } /** * @classdesc * This object provides persistent caching functionality. * The component is both private and restricted to framework core usage. It is currently supported to a limited set of environments: * <ul> * <li>Google Chrome(version >=49) for desktop</li> * <li>Internet Explorer(version >=11) for desktop.</li> * </ul> * For all other environments, a dummy (NOP) implementation will be loaded, see {@link sap.ui.core.cache.CacheManagerNOP}. * * This object is not meant for application developer's use, but for core UI5 framework purposes. * * The cache manager maps all entries to a single (current) UI5 version. If the cache is loaded * with different UI5 version, all existing entries will be deleted. * * Example usage: * <pre> * sap.ui.define(['sap/ui/core/cache/CacheManager'], * function(oCacheManager) { * oCacheManager.get("myKey").then(function(value){ * if (value) { * //process it * } else { * //obtain it and eventually store in the cache * var oEntry = new Equipment(); * oCacheManager.set("myKey", oEntry); * } * }); * } * }); * </pre> * @private * @ui5-restricted sap.ui.core * @since 1.40.0 * @namespace * @alias sap.ui.core.cache.CacheManager */ var CacheManager = { /** * Reference to the current underlying implementation * @private */ _instance: null, /** * Obtains a concreate implementation according to set of rules. * @returns {Promise} * @private */ _getInstance: function () { var pInstanceCreation, oMsr = startMeasurements("_getInstance"), that = this; pInstanceCreation = new Promise(function (resolve, reject) { var oInstance; Log.debug("Cache Manager: Initialization..."); if (!CacheManager._instance) { oInstance = that._findImplementation(); Measurement.start(S_MSR_INIT_IMPLEMENTATION, "CM", S_MSR_CAT_CACHE_MANAGER); oInstance.init().then(resolveCacheManager, reject); Measurement.end(S_MSR_INIT_IMPLEMENTATION, "CM"); } else { resolveCacheManager(CacheManager._instance); } function resolveCacheManager(instance) { CacheManager._instance = instance; oMsr.endAsync(); Log.debug("Cache Manager initialized with implementation [" + CacheManager._instance.name + "], resolving _getInstance promise"); resolve(instance); } }); oMsr.endSync(); return pInstanceCreation; }, /** * Determines which implementation should be used, based on a certain rules. * @returns {Object} the implementation * @private */ _findImplementation: function () { if (isSwitchedOn() && this._isSupportedEnvironment()) { return LRUPersistentCache; } else { Log.debug("UI5 Cache Manager is switched off"); return CacheManagerNOP; } }, /** * Stores or updates value for given key. * @param {string|number} key the key to associate the value with. Null is not accepted * @param {*} value any value that match the structured clone algorithm. Undefined is not accepted. * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm * @returns {Promise} a promise that would be resolved in case of successful operation or rejected with * value of the error message if the operation fails. * @public */ set: function (key, value) { var pSet, oMsr = startMeasurements("set", key); Log.debug("Cache Manager: Setting value of type[" + typeof value + "] with key [" + key + "]"); pSet = this._callInstanceMethod("set", arguments).then(function callInstanceHandler() { this.logResolved("set"); oMsr.endAsync(); //nothing to return, just logging. }.bind(this), function (e) { Log.error("Cache Manager: Setting key [" + key + "] failed. Error:" + e); oMsr.endAsync(); throw e; }); oMsr.endSync(); return pSet; }, /** * Retrieves a value for given key. * @param {string|number} key the key to retrieve a value for * @returns {Promise<any|undefined>} a promise that would be resolved in case of successful operation or rejected with * value of the error message if the operation fails. It resolves with a value that is either: * <ul> * <li>undefined - the entry does not exist</li> * <li>any other - the entry exists and value contains the actual one</li> * </ul> * @public */ get: function (key) { var pGet, fnDone = Interaction.notifyAsyncStep(), oMsr = startMeasurements("get", key); Log.debug("Cache Manager: Getting key [" + key + "]"); pGet = this._callInstanceMethod("get", arguments).then(function callInstanceHandler(v) { this.logResolved("get"); oMsr.endAsync(); return v; }.bind(this), function (e) { Log.debug("Cache Manager: Getting key [" + key + "] failed. Error: " + e); oMsr.endAsync(); throw e; }).finally(fnDone); oMsr.endSync(); return pGet; }, /** * Checks whether certain entry exists. * @param {string|number} key the key to look for. Null is not accepted. * @returns {Promise} a promise that would be resolved in case of successful operation or rejected with * value of the error message if the operation fails. It resolves with a boolean value of true - if an entry * with the given key is found, false - otherwise * @public */ has: function (key) { var pHas, oMsr = startMeasurements("has", key); Log.debug("Cache Manager: has key [" + key + "] called"); pHas = this._callInstanceMethod("has", arguments).then(function callInstanceHandler(result) { this.logResolved("has"); oMsr.endAsync(); return result; }.bind(this)); oMsr.endSync(); return pHas; }, /** * Deletes entry with given key. * @param {string|number} key the key to delete an entry for. Null is not accepted. * @returns {Promise} a promise that would be resolved in case of successful operation or rejected with * value of the error message if the operation fails. * @public */ del: function (key) { var pDel, oMsr = startMeasurements("del", key); Log.debug("Cache Manager: del called."); pDel = this._callInstanceMethod("del", arguments).then(function callInstanceHandler() { this.logResolved("del"); oMsr.endAsync(); //nothing to return, just logging. }.bind(this), function (e) { Log.debug("Cache Manager: del failed. Error: " + e); oMsr.endAsync(); throw e; }); oMsr.endSync(); return pDel; }, /** * Deletes entries, filtered using several criteria. * @param {object} [filters] The filters that will be applied to find the entries for deletion. If not * provided - all entries are included. * @param {string} [filters.prefix] Only includes entries that have a key starting with this prefix. * If not provided - entries with all keys are included. * @param {Date} [filters.olderThan] Only includes entries with older usage dates. If not provided * - entries with any/no usage date are included. * @returns {Promise} A promise that would be resolved in case of successful operation or rejected with * value of the error message if the operation fails. * @public * @since 1.102.0 */ delWithFilters: function(filters) { var pDel; Log.debug("Cache Manager: delWithFilters called."); pDel = this._callInstanceMethod("delWithFilters", arguments).then(function callInstanceHandler() { this.logResolved("delWithFilters"); }.bind(this), function (e) { Log.debug("Cache Manager: delWithFilters failed. Error: " + e); throw e; }); return pDel; }, /** * Clears all entries in the cache. * @returns {Promise} a promise that would be resolved in case of successful operation or rejected with * value of the error message if the operation fails. * @public */ reset: function () { var pReset, oMsr = startMeasurements("reset"); Log.debug("Cache Manager: Reset called."); pReset = this._callInstanceMethod("reset", arguments).then(function callInstanceHandler() { this.logResolved("reset"); oMsr.endAsync(); //nothing to return, just logging. }.bind(this), function (e) { Log.debug("Cache Manager: Reset failed. Error: " + e); oMsr.endAsync(); throw e; }); oMsr.endSync(); return pReset; }, /** * Shuts-down the Cache Manager (all next calls to it will return an immediately resolved dummy promise with value of <undefined>) * Usages are meant for testing purposes. Make sure you switch it on so the rest still can use it. * @see sap.ui.core.cache.CacheManager._switchOn * @returns {*} * @protected */ _switchOff: function () { var that = this; return Promise.resolve().then(function () { safeClearInstance(that); setUI5CacheOn(false); }); }, /** * Starts the Cache Manager (all next calls to it will work against real cache data) * Usages are meant for testing purposes. If its already on, nothing will happen * @returns {*} * @protected */ _switchOn: function () { var that = this; return Promise.resolve().then(function () { if (!isUI5CacheOn()) { safeClearInstance(that); setUI5CacheOn(true); } return Promise.resolve(); }); }, /** * Forwards method's call to the underlying implementation * @param {string} sMethodName the name of the method to forward * @param {any[]} aArgs array of arguments * @returns {Promise} * @private */ _callInstanceMethod: function (sMethodName, aArgs) { var pCallInstance, sMsrCallInstance = "[sync ] _callInstanceMethod"; Measurement.start(sMsrCallInstance, "CM", S_MSR_CAT_CACHE_MANAGER); if (this._instance) { Log.debug("Cache Manager: calling instance..."); return this._instance[sMethodName].apply(this._instance, aArgs); } Log.debug("Cache Manager: getting instance..."); pCallInstance = this._getInstance().then(function instanceResolving(instance) { return instance[sMethodName].apply(instance, aArgs); }); Measurement.end(sMsrCallInstance); return pCallInstance; }, /** * Checks whether the given environment is supported by the CacheManager. * @returns {boolean|*} true if yes, false if not. * @private */ _isSupportedEnvironment: function () { var aSupportedEnv = []; if (this._bSupportedEnvironment == undefined) { aSupportedEnv.push({ system: Device.system.SYSTEMTYPE.DESKTOP, browserName: Device.browser.BROWSER.CHROME, browserVersion: 49 }); aSupportedEnv.push({ system: Device.system.SYSTEMTYPE.DESKTOP, browserName: Device.browser.BROWSER.SAFARI, browserVersion: 13 }); aSupportedEnv.push({ system: Device.system.SYSTEMTYPE.TABLET, browserName: Device.browser.BROWSER.SAFARI, browserVersion: 13 }); aSupportedEnv.push({ system: Device.system.SYSTEMTYPE.PHONE, browserName: Device.browser.BROWSER.SAFARI, browserVersion: 13 }); aSupportedEnv.push({ system: Device.system.SYSTEMTYPE.TABLET, os: Device.os.OS.ANDROID, browserName: Device.browser.BROWSER.CHROME, browserVersion:80 }); aSupportedEnv.push({ system: Device.system.SYSTEMTYPE.PHONE, os: Device.os.OS.ANDROID, browserName: Device.browser.BROWSER.CHROME, browserVersion: 80 }); this._bSupportedEnvironment = aSupportedEnv.some(function (oSuppportedEnv) { var bSupportedSystem = Device.system[oSuppportedEnv.system], bSupportedOSName = oSuppportedEnv.os ? oSuppportedEnv.os === Device.os.name : true, bSupportedBrowserName = oSuppportedEnv.browserName === Device.browser.name, bSupportedBrowserVersion = Device.browser.version >= oSuppportedEnv.browserVersion; try { return bSupportedSystem && bSupportedOSName && bSupportedBrowserName && bSupportedBrowserVersion && window.indexedDB; } catch (error) { return false; } }); } return this._bSupportedEnvironment; }, logResolved: function(sFnName) { this._instance.logResolved && this._instance.logResolved(sFnName); } }; var S_MSR_CAT_CACHE_MANAGER = "CacheManager", S_MSR_INIT_IMPLEMENTATION = "[sync ] _initImplementation", iMsrCounter = 0; function isSwitchedOn() { return isUI5CacheOn(); } function safeClearInstance(cm) { if (cm._instance) { cm._instance._destroy(); cm._instance = null; } } function startMeasurements(sOperation, key) { iMsrCounter++; var sMeasureAsync = "[async] " + sOperation + "[" + key + "]- #" + (iMsrCounter), sMeasureSync = "[sync ] " + sOperation + "[" + key + "]- #" + (iMsrCounter); Measurement.start(sMeasureAsync, "CM", [S_MSR_CAT_CACHE_MANAGER, sOperation]); Measurement.start(sMeasureSync, "CM", [S_MSR_CAT_CACHE_MANAGER, sOperation]); return { sMeasureAsync: sMeasureAsync, sMeasureSync: sMeasureSync, endAsync: function () { Measurement.end(this.sMeasureAsync); }, endSync: function () { Measurement.end(this.sMeasureSync); } }; } return CacheManager; });