@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