<@doc> A catalog of all packages and kits that are installed on a %PROD% server. Instances of this class are created automatically by the system, and should not be created directly. (c) SAP AG 2003-2006. All rights reserved. Class KitsManifest; <@doc> Constructs and initializes a new KitsManifest object The kits manifest xml document constructor(xml) this.parseManifestXML(xml); end <@doc scope="private">Seed for configuration hash value calculation static readonly property primes = [1,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541]; ////////////////////////////////////////////////////////////////////////////////////// // PROPERTIES <@doc>Gets the list of errors encountered during parsing of the kits manifest document readonly property errors = ^String[]; <@doc>Gets the table of kits in the manifest, indexed by kit urn readonly property kits = ^KitInfo[urn]; <@doc>Gets the table of packages in the manifest, indexed by package name readonly property packages = ^PackageInfo[name]; <@doc>Gets the table of all declared aspect roles readonly property aspects = ^Object[name]; <@doc>Gets the table of all prefixes in use (both package and aspectRole prefixes) readonly property prefixes = ^String[pref]; <@doc>Gets the the server url of this manifest readonly property serverUrl = ''; <@doc>Gets the the server version of this manifest readonly property serverVer = ''; /////////////////////////////////////////////////////////////////////// // METHODS <@doc> Parses the kits manifest from a given XML document The kits manifest xml document Returns ~true if the manifest was parsed successfully; otherwise, returns ~false (use the @errors property to obtain the detailed list of errors) method parseManifestXML(xml) this.packages = {}; this.kits = {}; this.aspects = {}; this.prefixes = {}; this.errors = []; var kitslist = this.kits; try { if (!xml) throw new Error(-1, '#TEXT[XMSG_BAD_KIT_MANIFEST]'); this.serverUrl = xml.getAttribute('server_url'); this.serverVer = xml.getAttribute('server_version'); // first phase: scan and create all packages and all kits within each package var packages=xml.selectNodes('Package'), pkg; var kitnodes = {}; while (pkg = packages.nextNode()) { var pkgobj = node2obj(pkg, 'env:PackageInfo', '\@name', '\@prefix', '\@version', '\@label', 'Summary', 'Authors', 'Copyright'); if (!pkgobj.name) { this.errors.push('Missing package name'); continue; } if (pkgobj.name in this.packages) { this.errors.push('Duplicate package name: '+pkgobj.name); continue; } if (!pkgobj.version) pkgobj.version = '1.0.0'; if (!pkgobj.label) pkgobj.label = pkgobj.name; this.packages[pkgobj.name] = pkgobj; this.prefixes[pkgobj.prefix] = pkgobj.name; pkgobj.kits={}; var kits=pkg.selectNodes('Kit'), kit; while (kit = kits.nextNode()) { var kitobj = node2obj(kit, 'env:KitInfo', '\@name', '\@package', '\@type', '\@label', '\@icon', '\@isRTKit','Summary', 'Authors', 'Copyright'); if (!kitobj.name) { this.errors.push('Missing kit name in package '+pkgobj.name); continue; } kitobj.urn = pkgobj.name+':'+kitobj.name+'.kit'; if (kitobj.urn in kitslist) { this.errors.push('Duplicate kit name: '+kitobj.urn); continue; } kitobj.type = PREF(kitobj.type||'?'); kitobj.version = pkgobj.version; if (!kitobj.label) kitobj.label = kitobj.name; if ('PIES'.indexOf(kitobj.type) < 0) { this.errors.push(kitobj.name+" kit's type is invalid"); continue; } if (kitobj.package != pkgobj.name) { this.errors.push('Kit package name is incompatible with '+kitobj.name+' kit location: '); continue; } kitobj.package = pkgobj; kitobj.requiredPkgs = {}; kitobj.extendedKits = {}; kitobj.kitFeatures = {}; kitobj.kitImports = []; kitobj.fileImports = []; pkgobj.kits[kitobj.urn] = kitobj; kitslist[kitobj.urn] = kitobj; kitnodes[kitobj.urn] = kit; } } // second phase: rescan all kits and record their inter-dependencies for (var k in kitnodes) { var kitobj=kitslist[k] kitobj.icon = this.normalizeUrn(kitobj.icon, kitobj.package.name); var childs=kitnodes[k].childNodes, ch; while (ch = childs.nextNode()) { switch (ch.nodeName) { case 'Requires': var pkgname=ch.getAttribute('package'), version=ch.getAttribute('version'); var package=this.packages[pkgname]; if (!package || package.version < version) { this.errors.push('Package '+pkgname+' (V'+version+') required by '+kitobj.name+' kit is missing or outdated'); break; } kitobj.requiredPkgs[pkgname] = package; break; case 'Extends': var urn = this.normalizeUrn(ch.getAttribute('urn'), kitobj.package.name); if (!urn) break; if (!(urn in kitslist)) { this.errors.push('Bad or missing reference '+urn+' in '+kitobj.name+' kit'); break; } kitobj.extendedKits[urn] = kitslist[urn]; kitslist[urn].kitFeatures[kitobj.urn] = $ENV.createObject('env:KitFeature', kitobj, false, BOOL(ch.getAttribute('recommend'))); break; case 'Import': var urn = this.normalizeUrn(ch.getAttribute('urn'), kitobj.package.name); if (!urn) break; if (urn.search(/\.kit$/i) < 0) { kitobj.fileImports.push(urn); break; } if (!(urn in kitslist)) { this.errors.push('Bad or missing reference '+urn+' in '+kitobj.name+' kit'); break; } kitobj.kitImports.push(urn); kitobj.kitFeatures[urn] = $ENV.createObject('env:KitFeature', kitslist[urn], true, true); break; } } } // third phase: scan all aspect roles and record their prefixes var aspects=xml.selectNodes('AspectRole'), aspect; while (aspect = aspects.nextNode()) { var name = aspect.getAttribute('name'); var pref = aspect.getAttribute('prefix'); this.aspects[name] = {name:name, prefix:pref}; this.prefixes[pref] = name; } for (var urn in kitslist) { addDependencies(kitslist[urn]); kitslist[urn].dependents = {}; } for (var urn in kitslist) { var pkit=kitslist[urn], P=pkit.precedents; for (var urn2 in P) { kitslist[urn2].dependents[urn] = pkit; } } return true; } catch (e) {alert(e.message) this.errors.push(e.description); return false; } function addDependencies(kit) { if (kit.precedents && !ISEMPTY(kit.precedents)) return kit.precedents; kit.precedents = {}; for (var i=0, M=kit.kitImports, len=M.length; i Parses an XML document containing a configuration and validates the results The xml configuration document If ~true includes nested imported kits The parsed kits configuration sorted by loading order, or ~null in case of any errors Use the @errors property to obtain any errors that were encountered method parseConfigXML(xml, deep) if (deep === undefined) deep = false; this.errors = []; try { var config=[], visited={}, manifest=this; if (!xml) throw new Error(-1, '#TEXT[XMSG_BAD_KITS_CONFIG]'); var prodKitName = xml.getAttribute('production'); var prodKit = this.kits[prodKitName] || null; // verify the production kit is OK if (prodKit) { if (prodKit.version < xml.getAttribute('version')) { this.errors.push('#TEXT[XMSG_OUTDATED_PRODUCTION_KIT]'.replace('{0}',prodKitName).replace('{1}',xml.getAttribute('version')).replace('{2}',prodKit.version)); } } else { this.errors.push('#TEXT[XMSG_MISSING_PRODUCTION_KIT]'.replace('{0}',xml.getAttribute('production'))); var urn = xml.getAttribute('production'); var pkgname = urn.substr(0,urn.indexOf(':')); var package = this.packages[pkgname]; if (!package) { this.errors.push('#TEXT[XMSG_MISSING_PACKAGE]'.replace('{0}',pkgname)); } } // calculate the config based on the production kit that is named in the XML. if (0 == this.errors.length) { config = this.computeConfig(prodKit.urn); } else { config = null; } return(config); } catch (e) { this.errors.push(e.description); return false; } end <@doc> Produces an XML representation of a given kits configuration object The kits configuration object The xml representation of the kits configuration method printConfigXML(config) var A=[]; A.push(''); for (var i=0, len=config.length; i'); } A.push(''); return PARSEXML(JOIN(A)); end <@doc> Computes the kits configuration that fully encompasses a given production kit plus all feature kits The urn of the production kit The expanded kits configuration, sorted by loading order method computeConfig(prodkit) var features; prodkit = this.kits[prodkit]; if (!prodkit || prodkit.type != 'P') return []; var config=[], visited={}, manifest=this; // expand the production kit expandKit(prodkit); // sort the kit features by name to achieve consistent configurations var F=[]; //load all kits of this production kit features = this.getOrderedFeatures(prodkit.urn); for (var k in features) { if (!features[k] || features[k].required) continue; if (!(features[k].kit.urn in visited)) F.push(features[k].kit.urn); } F.sort(); // expand the remaining kit features until reaching the enclosing configuration for (var i=0, len=F.length; i Gets the list of files in a given kits configuration The kits configuration The list of files urns, sorted by loading order method getConfigFiles(config) var list=[]; for (var i=0, len=config.length; i Gets the list of feature kits for a given production kit, sorted by dependencies order The urn of the production kit The feature kits list, sorted by dependencies order, and with an empty entry between each layer of dependencies. method getOrderedFeatures(urn) var prodkit = this && this.kits[urn] || null, L=[], F={}; if (!prodkit || prodkit.type != 'P') return L; for (var k in prodkit.kitFeatures) F[k] = prodkit.kitFeatures[k]; addLayer(); return L; function addLayer() { var done=true, G={}; for (var k in F) { var P=F[k].kit.precedents, independent=true; for (var k2 in P) { if (k2 in F) { independent=false; break; } } if (independent) { G[k]=F[k]; done=false; } } if (done) return; if (L.length>0) L.push(null); for (var k in G) { L.push(F[k]); delete F[k]; } addLayer(); } end <@doc> Normalizes a given urn to a fully-qualified urn The urn to normalize The name of the package in context ~The normalized urn method normalizeUrn(urn, context) if (!urn) return ''; var k=(urn+'').indexOf(':'); if (k < 0) return context + ':' + urn; if (k > 0 && urn.charAt(0) == '~') return context + urn; return urn; end method isCorePackage(name) if (!name) return false ; if (name.substring(0,4) == 'core' || name == 'startUp' ) return true ; return false ; end