//@include lib_Class.js
//@include lib_sol.common.ObjectUtils.js
//@include lib_sol.common.RepoUtils.js
//@include lib_sol.common.UserUtils.js
//@include lib_sol.common.IxUtils.js
/*
* Local definition of the class `sol.common.Cache` for backward compatibility of previous solution packages.
*/
if (!sol.ClassManager.getClass("sol.common.Cache")) {
sol.define("sol.common.Cache", {
initialize: function (config) {
var me = this;
me.cache = new java.util.concurrent.ConcurrentHashMap(8, 0.9, 1);
},
/**
* Inserts the specified key-value pair into the cache.
* @param {String} key
* @param {Object} value
* @return {Object} The previous value associated with the key, or null if there was no mapping before
*/
put: function (key, value) {
var me = this;
return me.cache.put(key, value);
},
/**
* Inserts all key-value pairs specified by an object into the cache. Existing mappings will be replaced.
* @param {Object} data Property names will be used as keys and the associated values as values.
*/
putAll: function (data) {
var me = this;
me.cache.putAll(data);
},
/**
* Tests if the specified object is a key in the cache.
* @param {String} key
* @return {Boolean}
*/
containsKey: function (key) {
var me = this;
return me.cache.containsKey(key);
},
/**
* Returns the value for the specified key from the cache, or null if the chache contains no mapping for the key.
* @param {String} key
* @return {Object}
*/
get: function (key) {
var me = this;
return me.cache.get(key);
},
/**
* Returns an enumeration of all keys in the cache.
* @return {Object} An `java.util.Enumeration` of all keys
*/
keys: function () {
var me = this;
return me.cache.keys();
},
/**
* Returns a collection view of the values contained in the cache.
* @return {Object} An `java.util.Collection` of all values
*/
values: function () {
var me = this;
return me.cache.values();
},
/**
* Returns an enumeration of the values in the cache.
* @return {Object} An `java.util.Enumeration` of all values
*/
elements: function () {
var me = this;
return me.cache.elements();
},
/**
* Removes the key (and its corresponding value) from the cache.
* @param {String} key
* @return {Object} The previous value associated with the key, or null if there was no value for the key
*/
remove: function (key) {
var me = this;
return me.cache.remove(key);
},
/**
* Returns the number of key-value pairs in the cache.
* @return {Number}
*/
size: function () {
var me = this;
return me.cache.size();
},
/**
* Returns `true` if the chache contains no key-value pairs.
* @return {Boolean}
*/
isEmpty: function () {
var me = this;
return me.cache.isEmpty();
},
/**
* Removes all of the mappings from the cache.
*/
clear: function () {
var me = this;
me.cache.clear();
}
});
}
/**
* Helper for JSON configuration files.
*
* The constructor can take either a `load`, a `compose` parameter or a JavaScript object.
* If the configuration was loaded from the repository (with an additional `writable=true`) as parameter, all changes can be saved back to the repository with {@link #save}.
* Saving will always be deactivated if the configuration was loaded with compose.
* If the instance was constructed with an object, it can be saved as a new repository element with {@link #saveNew}.
*
* # Load
* This code loads a configuration in readonly mode from a repository file:
*
* var myconfig = sol.create("sol.common.Config", { load: "(286B8C55-DBF6-2391-8447-479ED57FFDB0)" }).config;
*
* The next example also loads a configuration, but also saves changed back:
*
* var cfg = sol.create("sol.common.Config", { load: "ARCPATH:/Administration/Configuration/MyJsonConfig", writable: true });
* var myconfig = cfg.config;
* // ... make some changes to myconfig ...
* cfg.save();
*
*
* # Compose
* If a new configuration object is created using `compose`,
* it tries to load the original file as well as a customized file from the same path underneath the 'Business Solutions Costom' folder.
* Both files will be merged, while the changes in the custom file will override the original settings.
*
* var myconfig = sol.create("sol.common.Config", { compose: "/contract/Configuration/contract.config" }).config;
*
* If `compose` is used with an objId, it tries to figure out the relative path inside the Solution folder.
* If there is none, nothing will be loaded.
* If there is more then one valid path the first one will be used.
*
* # Config
* To save a new configuration, you could:
*
* var cfg = sol.create("sol.common.Config", { config: { exampleConfigProperty: "a String", anotherProperty: 4711 } });
* cfg.saveNew("ARCPATH:/Administration/Configuration/myNewExampleConfig");
*
* # Additional comments
*
* If used within clients that don't have an ixConnectAdmin-connection configurations are processed by ix function calls.
* This allows for additional caching among all users and speed up client load times.
*
* # Protected config files
* If a config file contains sensible data (e.g. passwords) it can be secured by adding a `$protected` property (top level).
* This ensures that no user can obtain a configuration accidentally from cache, without having access to the file itself.
*
* @author PZ, ELO Digital Office GmbH
* @author NM, ELO Digital Office GmbH
* @version 1.06.000
*
* @elojc
* @eloas
* @eloix
* @requires sol.common.ObjectUtils
* @requires sol.common.RepoUtils
* @requires sol.common.UserUtils
* @requires sol.common.IxUtils
*/
sol.define("sol.common.Config", {
pilcrow: "\u00b6",
/**
* @private
* @property {String} CONFIG_PATH
*/
CONFIG_PATH: "ARCPATH[(E10E1000-E100-E100-E100-E10E10E10E00)]:/Business Solutions/common/Configuration/base.config",
/**
* @private
* @property {String[]} DEFAULT_BASE_PATHS
*/
DEFAULT_BASE_PATHS: [
"Business Solutions",
"Business Solutions Custom"
],
/**
* @cfg {String} load (optional) This can be an objId, GUID or arcpath to checkout a JSON configuration file from the repository.
*/
/**
* @cfg {String} compose (optional) This has to be a relative arcpath underneath the 'Business Solution' folder or a valid objId
*/
/**
* @cfg {Object} config (optional)
*/
/**
* @cfg {Boolean} [merge=false] (optional)
* If `true` the configuration loaded via `load`, will be merged (if possible).
* This is only neccessary if constructor is invoked with `load`, other wise it will be ignored.
*/
/**
* @cfg {Boolean} [forceReload=false] (optional) If `true` cache will be refreshed.
*/
/**
* @cfg {Boolean} [copy=false] (optional) If `true`, `config`-property of initialized class will contain a new object instead of a cache reference.
*/
/**
* @cfg {Boolean} [writable=false] (optional) If `true`, the loaded configuration is writable via the {@link #save} function
*/
/**
* @cfg {Boolean} [exceptionOnBrokenConfig=false] (optional) If `true` the merge function throws an exception if on of the configs (which should be merged) is broken.
*/
/**
* @cfg {de.elo.ix.client.IXConnection} connection IX connection
*/
/**
* @property {Object} config The loaded (or via constructor initialized) configuration object.
*/
/**
* @private
* @property {String[]} basePaths
*/
/**
* @private
* @property {String} objId Reference to the loaded configuration in the repository
*/
/**
* @private
* @property {sol.Logger} log Internal logger
*/
/**
* @private
* @property {String} eloAsPathPattern
* Describes the paths where the `loadEloAsConfig` function looks for the ELOas configuration (inside the Solution folder)
*/
eloAsPathPattern: "/{{SOLUTION}}/Configuration/as.config",
initialize: function (config) {
var me = this;
me.log = sol.create("sol.Logger", { scope: "sol.common.Config" });
me.merge = config.merge;
me.copy = config.copy;
me.forceReload = config.forceReload;
me.exceptionOnBrokenConfig = config.exceptionOnBrokenConfig;
me.conn = config.connection || ixConnect;
me.log.enter("initilialize configuration", config);
if (config.load && (config.merge !== true)) {
me.objId = config.load;
// load remotely if running in clients
me.isRemote() ? me.reloadRemote(me.forceReload) : me.reload(me.forceReload);
} else if (config.compose || (config.load && (config.merge === true))) {
// load remotely if running in clients
if (me.isRemote()) {
me.compose = config.compose;
me.reloadRemote(me.forceReload);
} else {
me.compose = me.getCompose(config);
me.reload(me.forceReload);
}
} else if (config.config) {
me.config = config.config;
me.objId = config.objId;
}
if (config.writable === true && !me.compose) {
me.writable = true;
}
me.config = me.copy && me.rawConfig || me.config;
me.log.exit("initilialize configuration");
},
/**
* This function identifies if a configuration should be loaded remotely.
* @private
* @return {boolean} true if configuration should load remotely
*/
isRemote: function () {
return (typeof ixConnectAdmin === "undefined")
&& !!sol.ClassManager.getClass("sol.common.IxUtils");
},
/**
* This function reloads the configuration from the repository, and updates the `config` property remotely. This will allow caching configurations for all users in clients.
* @param {Boolean} [force=false] (optional) If `true`, cached configs will be ignored.
*/
reloadRemote: function (force) {
var me = this,
cacheKey = me.objId || me.compose, rawCacheKey = cacheKey + "_raw",
cachedCfg, configObj, rawConfigStr;
me.log.enter("load configuration remote");
cachedCfg = sol.common.ConfigCache.get(cacheKey);
if (!cachedCfg || force === true) {
if (me.objId) {
me.log.info(["load configuration remotely: ", me.objId]);
configObj = sol.common.IxUtils.execute("RF_sol_common_service_GetConfig", {
objId: me.objId,
forceReload: force
}).config;
rawConfigStr = JSON.stringify(configObj);
} else if (me.compose) {
// retrieve merged config using ix interface
me.log.info(["load merged configuration remotely: ", me.compose]);
configObj = sol.common.IxUtils.execute("RF_sol_common_service_GetMergedConfig", {
compose: me.compose,
forceReload: force
}).config;
rawConfigStr = JSON.stringify(configObj);
}
sol.common.ConfigCache.put(cacheKey, configObj); // cache reference to object
me.config = configObj; // give access to reference
sol.common.ConfigCache.put(rawCacheKey, rawConfigStr); // cache string
me.rawConfig = me.copy && JSON.parse(rawConfigStr); // give access to parsed-string -> returns a brand-new object
} else {
me.config = cachedCfg;
me.rawConfig = me.copy && JSON.parse(String(sol.common.ConfigCache.get(rawCacheKey)));
}
me.log.exit("load configuration");
},
/**
* This function reloads the configuration from the repository, and updates the `config` property
* @param {Boolean} [force=false] (optional) If `true`, cached configs will be ignored.
*/
reload: function (force) {
var me = this,
cacheKey = me.objId || me.compose, rawCacheKey = cacheKey + "_raw",
cachedCfg, configObj, rawConfigStr, mergeHierarchy, mergeObjects;
me.log.enter("load configuration");
cachedCfg = sol.common.ConfigCache.getProtected(cacheKey);
if (!cachedCfg || force === true) {
if (me.objId) {
me.log.debug(["load configuration in simple mode (objId={0})", me.objId]);
try {
rawConfigStr = sol.common.RepoUtils.downloadToString(me.objId, null, { connection: me.conn });
configObj = JSON.parse(rawConfigStr);
} catch (ex) {
me.log.error("could not parse configuration");
throw "Configuration error: " + ex;
}
} else if (me.compose) {
mergeHierarchy = me.retrieveMergeHierarchy(true);
mergeObjects = [];
me.log.debug(["load configuration in merge mode (compose={0}), mergeing '{1}' configs", me.compose, mergeHierarchy.length]);
mergeHierarchy.forEach(function (mergeObj) {
if (mergeObj) {
try {
mergeObjects.push(JSON.parse(sol.common.RepoUtils.downloadToString(mergeObj.guid, null, { connection: me.conn })));
} catch (ex) {
me.log.warn(["no valid config in '{0}'", mergeObj.guid], ex);
if (me.exceptionOnBrokenConfig === true) {
throw "Error loading config file '" + mergeObj.arcPath + "'";
}
}
}
});
configObj = sol.common.ObjectUtils.mergeObjects(mergeObjects.shift(), mergeObjects);
rawConfigStr = JSON.stringify(configObj);
}
sol.common.ConfigCache.put(cacheKey, configObj); // cache reference to object
me.config = configObj; // give access to reference
sol.common.ConfigCache.put(rawCacheKey, rawConfigStr); // cache string
me.rawConfig = me.copy && JSON.parse(rawConfigStr); // give access to parsed-string -> returns a brand-new object
} else {
me.config = cachedCfg;
me.rawConfig = me.copy && JSON.parse(String(sol.common.ConfigCache.get(rawCacheKey)));
}
me.log.exit("load configuration");
},
/**
* This function saves changes to the `config` property to the repository
* @throws Throws an exception, if there is no reference to an repository element (see {@link #objId})
* @throws Throws an exception, if the oebjct was created in readonly mode (see {@link #writable})
*/
save: function () {
var me = this,
fileContent;
me.log.enter("save configuration");
if (!me.writable) {
throw "Config in readonly mode";
}
if (!me.objId) {
throw "no target path";
}
fileContent = new java.lang.String(JSON.stringify(me.config, null, 2));
sol.common.RepoUtils.uploadSmallContent(me.objId, fileContent);
me.log.exit("save configuration");
},
/**
* Saves a new configuration file to the repository.
*
* - <b>The path has to contain the element name</b>
* - <b>The path needs to exist (except for the element name), i.e. no new folders will be created</b>
*
* @param {String} arcPath An repositoty path.
* @param {Object} config Configuration
* @param {de.elo.ix.client.IXConnection} config.connection Index server connection
*/
saveNew: function (arcPath, config) {
var me = this,
conn;
me.log.enter("save new configuration");
config = config || {};
conn = config.connection || ixConnect;
if (!arcPath) {
throw "Repository path is missing.";
}
sol.common.RepoUtils.saveToRepo({ repoPath: arcPath, maskId: CONST.DOC_MASK.GUID_ELOSCRIPTS, contentObject: me.config, connection: conn });
me.log.exit("save new configuration");
},
/**
* Retrieves all configs (objIds) for a given element which would be merged to it.
*
* cfg.retrieveMergeHierarchy() // => ["(7AEC9AD9-A487-F472-1C60-24925943A3CB)"]
*
* cfg.retrieveMergeHierarchy(true) // => [{ guid: "(7AEC9AD9-A487-F472-1C60-24925943A3CB)", basePath: "Business Solutions", arcPath: "ARCPATH:/..." }]
*
* @param {boolean} [extended=false] If `true`, this function returns additional infos.
* @return {String[]|Object[]} Array with GUIDs
*/
retrieveMergeHierarchy: function (extended) {
var me = this,
mergeHierarchy = [],
composePath;
me.log.enter("retrieve hierarchy");
composePath = (me.compose) ? me.compose : me.retrieveComposePath(me.objId);
if (composePath) {
me.getBasePaths().forEach(function (basePath) {
var sep, arcpath, sord;
try {
sep = composePath.charAt(0);
arcpath = "ARCPATH[(E10E1000-E100-E100-E100-E10E10E10E00)]:" + sep + basePath + composePath;
sord = me.conn.ix().checkoutSord(arcpath, SordC.mbOnlyId, LockC.NO);
if (extended === true) {
mergeHierarchy.push({
guid: sord.guid,
basePath: basePath,
arcPath: arcpath
});
} else {
mergeHierarchy.push(sord.guid);
}
} catch (ex) {
mergeHierarchy.push(null);
me.log.info(["could not determine guid for arcpath='{0}'", arcpath], ex);
}
});
}
me.log.exit("retrieve hierarchy");
return mergeHierarchy;
},
/**
* Retrieves the relative path for an object, which can be used to compose.
* If there is more then one composable path, the first one will be used.
* @param {String} objId
* @return {String}
*/
retrieveComposePath: function (objId) {
var me = this,
conn, sordZ, sord, validPath, refPaths;
conn = (typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect;
sordZ = new SordZ(SordC.mbMin);
sordZ.add(SordC.mbRefPaths);
sord = conn.ix().checkoutSord(objId, sordZ, LockC.NO);
refPaths = Array.prototype.slice.call(sord.refPaths); // Conversion to JavaScript array for Nashorn compatibility
refPaths.some(function (refpath) {
var valid = false;
if (!refpath.path || refpath.path.length <= 2) {
return false;
}
if (refpath.path[0].guid != "(E10E1000-E100-E100-E100-E10E10E10E00)") {
return;
}
valid = me.getBasePaths().some(function (entry) {
return entry == refpath.path[1].name;
});
if (valid) {
validPath = refpath;
return true;
}
});
if (validPath) {
validPath = Array.prototype.slice.call(validPath.path, 2); // Nashorn compatible call
validPath = me.pilcrow + validPath.map(function (e) {
return e.name;
}).join(me.pilcrow) + me.pilcrow + sord.name;
me.log.debug(["Compose path for objId={0}: {1}", objId, validPath]);
return validPath;
}
return null;
},
/**
* Checks, if an object is in a valid location, so it can be used for `compose` mode.
* @param {String} objId (optional) If `undefined` the internal value (from constructor: `load` or `compose`) will be used
* @return {Boolean}
*/
validForMergeing: function (objId) {
var me = this,
validPath;
if (!objId && !me.objId) {
return !!me.compose;
}
objId = objId || me.objId;
validPath = me.retrieveComposePath(objId);
return (validPath !== null);
},
/**
* Loads the ELOas configuration. The filename has to be `as.config`
*
* The `as.config` file content has to be a valid JSON string with at least the following propperties:
*
* {
* "protocol": "http",
* "server": "myservername",
* "port": "8080",
* "name": "as-myarchive"
* }
*
* If a `solution` string is provided, the function tries to lookup a solution specific ELOas configuration.
* If no specific configuration is found, or no `solution` string is provied, it falls back to the `common` configuration.
*
* Lookup path:
*
* "ARCPATH:/Administration/Business Solutions/{{SOLUTION}}/Configuration/as.config"
*
* Fallback path:
*
* "ARCPATH:/Administration/Business Solutions/common/Configuration/as.config"
*
* The following examle tries to load an ELOas configuration specific for the 'invoice' solution:
*
* var eloAsConfig = sol.create("sol.common.Config").loadEloAsConfig("invoice");
*
* The result for the configuration file above:
*
* {
* protocol: "http",
* server: "myservername",
* port: "8080",
* name: "as-myarchive"
* }
*
* This uses the {@link sol.common.ConfigCache}.
* First it tries to retrieve a configuration for the solution name.
* If none was found it attempts to read the configuration from a solution specific file.
* If there is no solution specific ELOas configuration (neither in cache, nor in the archive),
* it will try to use a cached common configuration or get the one from the archive.
*
* @param {String} solution (optional)
* @return {Object}
*/
loadEloAsConfig: function (solution) {
var me = this,
oldCompose, oldObjId, eloAsConfig, result;
me.log.enter("load ELOas configuration");
if (solution) {
eloAsConfig = sol.common.ConfigCache.getELOasCfg(solution);
if (!eloAsConfig) {
try {
oldCompose = me.compose;
oldObjId = me.objId;
me.objId = null;
me.compose = me.eloAsPathPattern.replace("{{SOLUTION}}", solution);
me.reload(me.forceReload);
eloAsConfig = me.config;
sol.common.ConfigCache.putELOasCfg(solution, eloAsConfig);
} catch (ex) {
if (solution == "common") {
me.log.warn("no ELOas configuration found in 'common'");
} else {
me.log.warn(["no solution specific ELOas configuration found for '{0}'", solution]);
}
} finally {
me.objId = oldObjId;
me.compose = oldCompose;
me.reload(me.forceReload);
}
}
}
if (sol.common.ObjectUtils.isEmpty(eloAsConfig) && (solution != "common")) {
me.log.debug("try fallback to 'common'");
eloAsConfig = me.loadEloAsConfig("common");
}
result = (sol.common.ObjectUtils.isObject(eloAsConfig)) ? eloAsConfig : null;
me.log.exit("load ELOas configuration");
return result;
},
/**
* @private
* Retrieves the base paths from the common.config.baseMergePaths or uses {@link #DEFAULT_BASE_PATHS} as fallback.
* @return {String}
*/
getBasePaths: function () {
var me = this,
commonCfg;
if (!me.basePaths) {
try {
commonCfg = sol.create("sol.common.Config", { load: me.CONFIG_PATH }).config;
me.basePaths = (commonCfg && commonCfg.baseMergePaths) ? commonCfg.baseMergePaths : me.DEFAULT_BASE_PATHS;
} catch (ex) {
me.basePaths = me.DEFAULT_BASE_PATHS;
}
}
return me.basePaths;
},
/**
* @private
* Retrieves the compose path from the constructors config object.
* @param {Object} config
* @return {String}
*/
getCompose: function (config) {
var me = this,
checkObjId;
if (config.compose) {
checkObjId = me.getObjId(config.compose);
return (checkObjId) ? me.retrieveComposePath(config.compose) : config.compose;
}
if (config.load && me.validForMergeing(config.load)) {
return me.retrieveComposePath(config.load);
}
return null;
},
/**
* @private
* Returns the object ID of a given repository path.
* @param {String} path Repository path. The path separator is defined by the first character after "ARCPATH:"
* @return {String} The ID of the new element, or nothing if it does not exist
*/
getObjId: function (path) {
var conn, sord;
if (sol.common.RepoUtils.isObjId(path) || sol.common.RepoUtils.isGuid(path)) {
return path;
}
try {
conn = (typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect;
sord = conn.ix().checkoutSord(path, SordC.mbOnlyId, LockC.NO);
return sord.id;
} catch (ignore) {
// Object not found
}
}
});
/**
* Caching for configuration files.
*
* Cache is disabled for administrative users. If an administrative user requests a cached config this class always returns `null`.
*
* @elojc
* @eloas
* @eloix
*
* @author PZ, ELO Digital Office GmbH
* @version 1.03.000
*/
sol.define("sol.common.ConfigCache", {
singleton: true,
requires: ["sol.common.Cache"],
/**
* @private
* @property {sol.common.Cache} cache This contains the already loaded configurations
*/
cache: sol.create("sol.common.Cache"),
eloAsKeyPrefix: "ELOASCONFIG#",
/**
* Retrieves a configuration from the cache.
* @param {String} key
* @return {Object}
*/
get: function (key) {
var me = this,
cachedCfg = null;
if (key === null) {
throw "Can not access config cache with an 'null' as key";
}
if (!me.cacheDisabled() && me.cache.containsKey(key)) {
me.logger.debug(["cache hit for key={0}", key]);
cachedCfg = me.cache.get(key);
}
return cachedCfg;
},
/**
* Retrieves a configuration from the cache. Configs with protected flag will only delivered from cache for service users.
* @param {String} key
* @return {Object}
*/
getProtected: function (key) {
var me = this,
cachedCfg = null;
cachedCfg = me.get(key);
return (cachedCfg && !cachedCfg.hasOwnProperty("$protected")) ? cachedCfg : null;
},
/**
* Puts a configuration into the cache.
* @param {String} key
* @param {Object} cfg
*/
put: function (key, cfg) {
var me = this;
if (key && cfg) {
me.logger.debug(["put key={0} into cache", key]);
me.cache.put(key, cfg);
}
},
/**
* Retrieves an ELOas configuration from the cache.
* @param {String} solution
* @return {Object}
*/
getELOasCfg: function (solution) {
var me = this;
me.logger.debug(["load ELOas '{0}' config from cache", solution]);
return me.get(me.eloAsKeyPrefix + solution);
},
/**
* Puts an ELOas configuration into the cache.
* @param {String} solution
* @param {Object} cfg
*/
putELOasCfg: function (solution, cfg) {
var me = this;
if (solution && cfg) {
me.logger.debug(["put ELOas '{0}' config into cache", solution]);
me.cache.put(me.eloAsKeyPrefix + solution, cfg);
}
},
/**
* @private
* Checks, if caching is disabled.
* Currently caching is only disabled for administrative users with interactive login flag.
* @return {Boolean}
*/
cacheDisabled: function () {
var me = this,
userInfo, isDisabled;
if (sol.common.UserUtils && sol.common.UserUtils.isMainAdmin && sol.common.UserUtils.isServiceUser) {
userInfo = ixConnect.loginResult.user;
isDisabled = sol.common.UserUtils.isMainAdmin(userInfo) && !sol.common.UserUtils.isServiceUser(userInfo);
}
if (isDisabled) {
me.logger.debug("caching disabled for administrative users");
}
return isDisabled;
}
});
/**
* Utility functions to mix into objects and help working with configurations.
*
* @elojc
* @eloas
* @eloix
*
* @requires sol.common.Config
* @requires sol.common.ObjectUtils
* @requires sol.common.StringUtils
*/
sol.define("sol.common.ConfigMixin", {
singleton: true,
mixin: true,
/**
* Parses the configuration from an Object or a JSON String.
*
* Additionally, a JSON file can be specified, to load configuration from.
*
* {
* $config: "4711", // (optional) an objId, GUID or ARCPATH to a JSON file
* $property: "configParts.part1", // (optional) if specified, and the property is an Object, this will be used, instead of the hole JSON file content (can reference sub-objects by using '.' notation)
* extraParam: "extra" // (optional) all properties, without `$` prefix, will be written into the result.config Object (possibly overwriting values from the JSON file)
* }
*
* If the JSON file (with objId=4711) would contain something like this:
*
* {
* "someProperty": "string",
* "configParts": {
* "part1": {
* "firstParam": "first",
* "secondParam": "second"
* }
* }
* }
*
* The returned Object would look like this:
*
* {
* $config: "4711",
* $property: "configParts.part1",
* config: {
* firstParam: "first",
* secondParam: "second",
* extraParam: "extra"
* }
* }
*
* @param {Object|String} configuration A JavaScript Object, or a JSON String
* @param {Boolean} allProps
* @return {Object}
*/
parseConfiguration: function (configuration, allProps, copy) {
var me = this,
configObj, configInstance, config, prop;
configObj = (sol.common.ObjectUtils.isObject(configuration)) ? configuration : JSON.parse(configuration);
// load the config from a JSON file
if (configObj && configObj.$config) {
configInstance = sol.create("sol.common.Config", { compose: configObj.$config, copy: !!copy });
if (!configInstance.validForMergeing()) {
configInstance = sol.create("sol.common.Config", { load: configObj.$config, copy: !!copy }); // config is not in merge hierarchy -> reload in simple mode
}
config = configInstance.config;
}
// use only the property part of the loaded config
if (config && configObj.$property) {
config = me.extractConfigPart(config, configObj.$property);
}
if (!config) {
config = {};
}
// copy remaining properties to config (override properties from the file)
for (prop in configObj) {
if (configObj.hasOwnProperty(prop) && (!sol.common.StringUtils.startsWith(prop, "$") || allProps)) {
config[prop] = configObj[prop];
}
}
configObj.config = config;
return configObj;
},
* @private
* Extracts a part of a bigger configuration object.
* @param {Object} config The hole configuration object
* @param {String} property The property (or path in dot notation), which holds the desired property part
* @return {Object}
*/
extractConfigPart: function (config, property) {
var configPart = property.split(".").reduce(function (obj, key) {
return obj[key];
}, config);
if (sol.common.ObjectUtils.isObject(configPart)) {
return configPart;
}
},
/**
* Merge configuration
* @param {Object|String} configuration A JavaScript Object, or a JSON String
* @return {Object} merged configuration
*/
mergeConfiguration: function (configuration) {
var me = this;
return me.parseConfiguration(configuration, true).config;
}
});