pkgimpl.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifndef WUPACKAGE_IMPL_HPP
  14. #define WUPACKAGE_IMPL_HPP
  15. #include "platform.h"
  16. #include "jprop.hpp"
  17. #include "jptree.hpp"
  18. #include "jregexp.hpp"
  19. #include "package.h"
  20. #include "referencedfilelist.hpp"
  21. class CPackageSuperFileArray : public CInterface, implements ISimpleSuperFileEnquiry
  22. {
  23. IArrayOf<IPropertyTree> subFiles;
  24. public:
  25. IMPLEMENT_IINTERFACE;
  26. CPackageSuperFileArray(IPropertyTreeIterator &_subs)
  27. {
  28. ForEach(_subs)
  29. {
  30. IPropertyTree &sub = _subs.query();
  31. sub.Link();
  32. subFiles.append(sub);
  33. }
  34. }
  35. virtual unsigned numSubFiles() const
  36. {
  37. return subFiles.length();
  38. }
  39. virtual bool getSubFileName(unsigned num, StringBuffer &name) const
  40. {
  41. if (subFiles.isItem(num))
  42. {
  43. name.append(subFiles.item(num).queryProp("@value"));
  44. return true;
  45. }
  46. else
  47. return false;
  48. }
  49. virtual unsigned findSubName(const char *subname) const
  50. {
  51. ForEachItemIn(idx, subFiles)
  52. {
  53. if (stricmp(subFiles.item(idx).queryProp("@value"), subname))
  54. return idx;
  55. }
  56. return NotFound;
  57. }
  58. virtual unsigned getContents(StringArray &contents) const
  59. {
  60. ForEachItemIn(idx, subFiles)
  61. {
  62. contents.append(subFiles.item(idx).queryProp("@value"));
  63. }
  64. return subFiles.length();
  65. }
  66. };
  67. class WORKUNIT_API CPackageNode : extends CInterface, implements IHpccPackage
  68. {
  69. protected:
  70. Owned<IPropertyTree> node;
  71. Owned<IProperties> mergedEnvironment;
  72. hash64_t hash;
  73. void mergeEnvironment(const CPackageNode *base);
  74. virtual IPropertyTreeIterator *lookupElements(const char *xpath1, const char *xpath2) const = 0;
  75. inline StringBuffer makeSuperFileXPath(StringBuffer &xpath, const char *superFileName) const
  76. {
  77. superFileName = skipForeign(superFileName);
  78. return xpath.append("SuperFile[@id='").appendLower(strlen(superFileName), superFileName).append("']");
  79. }
  80. ISimpleSuperFileEnquiry *resolveSuperFile(const char *superFileName) const;
  81. inline bool hasProp(const char *xpath) const
  82. {
  83. return (node) ? node->hasProp(xpath) : false;
  84. }
  85. virtual const char *queryId() const
  86. {
  87. return (node) ? node->queryProp("@id") : NULL;
  88. }
  89. virtual bool isCompulsory() const
  90. {
  91. return (node) ? node->getPropBool("@compulsory", false) : false;
  92. }
  93. virtual bool getSysFieldTranslationEnabled() const {return false;}
  94. virtual bool getEnableFieldTranslation() const
  95. {
  96. const char *val = queryEnv("control:enableFieldTranslation");
  97. if (!val) val = queryEnv("enableFieldTranslation"); // Backward compatibility
  98. if (val)
  99. return strToBool(val);
  100. else
  101. return getSysFieldTranslationEnabled();
  102. }
  103. CPackageNode()
  104. {
  105. hash = 0;
  106. }
  107. virtual const IHpccPackage *queryRootPackage()
  108. {
  109. return NULL;
  110. }
  111. public:
  112. IMPLEMENT_IINTERFACE;
  113. CPackageNode(IPropertyTree *p);
  114. ~CPackageNode()
  115. {
  116. }
  117. // Load mergedEnvironment from local XML node
  118. void loadEnvironment();
  119. virtual const char *queryEnv(const char *varname) const
  120. {
  121. return mergedEnvironment->queryProp(varname);
  122. }
  123. virtual hash64_t queryHash() const
  124. {
  125. return hash;
  126. }
  127. virtual const IPropertyTree *queryTree() const
  128. {
  129. return node;
  130. }
  131. virtual bool validate(StringArray &warn, StringArray &err) const;
  132. };
  133. enum baseResolutionState
  134. {
  135. basesUnresolved=0,
  136. basesResolving=1,
  137. basesResolved=2
  138. };
  139. template <class TYPE>
  140. class CResolvedPackage : public TYPE
  141. {
  142. public:
  143. typedef CResolvedPackage<TYPE> self;
  144. CIArrayOf<self> bases;
  145. baseResolutionState baseResolution;
  146. CResolvedPackage<TYPE>(IPropertyTree *p) : TYPE(p), baseResolution(basesUnresolved) {}
  147. virtual aindex_t getBaseCount() const {return bases.length();}
  148. const self *getResolvedBase(aindex_t pos) const
  149. {
  150. if (pos < getBaseCount())
  151. return &bases.item(pos);
  152. return NULL;
  153. }
  154. virtual const TYPE *getBaseNode(aindex_t pos) const {return (const TYPE *) getResolvedBase(pos);}
  155. void appendBase(const IHpccPackage *base)
  156. {
  157. if (base)
  158. {
  159. const self *p = dynamic_cast<const self *>(base);
  160. bases.append(const_cast<self &>(*LINK(p))); // should really be an arrayof<const base> but that would require some fixing in jlib
  161. TYPE::hash = pkgHash64Data(sizeof(p->hash), &p->hash, TYPE::hash);
  162. TYPE::mergeEnvironment(p);
  163. }
  164. }
  165. void resolveBases(const IHpccPackageMap *packages)
  166. {
  167. if (baseResolution==basesResolved)
  168. return;
  169. if (baseResolution==basesResolving)
  170. throw MakeStringExceptionDirect(0, "PACKAGE_ERROR: circular or invalid base package definition");
  171. TYPE::loadEnvironment();
  172. if (packages)
  173. {
  174. Owned<IPropertyTreeIterator> baseIterator = TYPE::node->getElements("Base");
  175. if (!baseIterator->first())
  176. appendBase(TYPE::queryRootPackage());
  177. else
  178. {
  179. baseResolution=basesResolving;
  180. do
  181. {
  182. IPropertyTree &baseElem = baseIterator->query();
  183. const char *baseId = baseElem.queryProp("@id");
  184. if (!baseId)
  185. throw MakeStringException(PACKAGE_MISSING_ID, "PACKAGE_ERROR: base element missing id attribute");
  186. const IHpccPackage *base = packages->queryPackage(baseId);
  187. if (!base)
  188. throw MakeStringException(PACKAGE_NOT_FOUND, "PACKAGE_ERROR: base package %s not found", baseId);
  189. CResolvedPackage<TYPE> *rebase = dynamic_cast<CResolvedPackage<TYPE> *>(const_cast<IHpccPackage *>(base));
  190. rebase->resolveBases(packages);
  191. appendBase(base);
  192. }
  193. while(baseIterator->next());
  194. }
  195. baseResolution=basesResolved;
  196. }
  197. }
  198. // Search this package and any bases for an element matching xpath1, then return iterator for its children that match xpath2
  199. IPropertyTreeIterator *lookupElements(const char *xpath1, const char *xpath2) const
  200. {
  201. IPropertyTree *parentNode = TYPE::node->queryPropTree(xpath1);
  202. if (parentNode)
  203. return parentNode->getElements(xpath2);
  204. ForEachItemIn(idx, bases)
  205. {
  206. const self &basePackage = bases.item(idx);
  207. IPropertyTreeIterator *it = basePackage.lookupElements(xpath1, xpath2);
  208. if (it)
  209. return it;
  210. }
  211. return NULL;
  212. }
  213. const char *locateSuperFile(const char *superFileName) const
  214. {
  215. if (!superFileName || !*superFileName || !TYPE::node)
  216. return NULL;
  217. StringBuffer xpath;
  218. if (TYPE::hasProp(TYPE::makeSuperFileXPath(xpath, superFileName)))
  219. return TYPE::queryId();
  220. ForEachItemIn(idx, bases)
  221. {
  222. if (bases.item(idx).hasProp(xpath))
  223. return bases.item(idx).queryId();
  224. }
  225. return NULL;
  226. }
  227. bool hasSuperFile(const char *superFileName) const
  228. {
  229. if (locateSuperFile(superFileName))
  230. return true;
  231. return false;
  232. }
  233. virtual bool validate(StringArray &warn, StringArray &err) const
  234. {
  235. return TYPE::validate(warn, err);
  236. }
  237. };
  238. typedef CResolvedPackage<CPackageNode> CHpccPackage;
  239. //================================================================================================
  240. // CPackageMap - an implementation of IPackageMap using a string map
  241. //================================================================================================
  242. template <class TYPE, class IFACE>
  243. class CPackageMapOf : public CInterface, implements IHpccPackageMap
  244. {
  245. public:
  246. typedef CResolvedPackage<TYPE> packageType;
  247. MapStringToMyClassViaBase<packageType, IFACE> packages;
  248. StringAttr packageId;
  249. StringAttr querySet;
  250. bool active;
  251. StringArray wildMatches, wildIds;
  252. public:
  253. IMPLEMENT_IINTERFACE;
  254. CPackageMapOf(const char *_packageId, const char *_querySet, bool _active)
  255. : packageId(_packageId), querySet(_querySet), active(_active), packages(true)
  256. {
  257. }
  258. // IPackageMap interface
  259. virtual bool isActive() const
  260. {
  261. return active;
  262. }
  263. virtual const packageType *queryResolvedPackage(const char *name) const
  264. {
  265. return name ? packages.getValue(name) : NULL;
  266. }
  267. virtual const IHpccPackage *queryPackage(const char *name) const
  268. {
  269. return name ? (IFACE*)packages.getValue(name) : NULL;
  270. }
  271. virtual const char *queryPackageId() const
  272. {
  273. return packageId;
  274. }
  275. virtual const packageType *matchResolvedPackage(const char *name) const
  276. {
  277. if (name && *name)
  278. {
  279. const packageType *pkg = queryResolvedPackage(name);
  280. if (pkg)
  281. return pkg;
  282. const char *tail = name + strlen(name)-1;
  283. while (tail>name && isdigit(*tail))
  284. tail--;
  285. if (*tail=='.' && tail>name)
  286. {
  287. StringAttr notail(name, tail-name);
  288. pkg = queryResolvedPackage(notail);
  289. if (pkg)
  290. return pkg;
  291. }
  292. ForEachItemIn(idx, wildMatches)
  293. {
  294. if (WildMatch(name, wildMatches.item(idx), true))
  295. return queryResolvedPackage(wildIds.item(idx));
  296. }
  297. }
  298. return NULL;
  299. }
  300. const IHpccPackage *matchPackage(const char *name) const
  301. {
  302. return (IFACE *) matchResolvedPackage(name);
  303. }
  304. void load(IPropertyTree *xml)
  305. {
  306. if (!xml)
  307. return;
  308. Owned<IPropertyTreeIterator> allpackages = xml->getElements("Package");
  309. ForEach(*allpackages)
  310. {
  311. IPropertyTree &packageTree = allpackages->query();
  312. const char *id = packageTree.queryProp("@id");
  313. if (!id || !*id)
  314. throw MakeStringException(PACKAGE_MISSING_ID, "Invalid package map - Package element missing id attribute");
  315. Owned<packageType> package = new packageType(&packageTree);
  316. packages.setValue(id, package.get());
  317. const char *queries = packageTree.queryProp("@queries");
  318. if (queries && *queries)
  319. {
  320. wildMatches.append(queries);
  321. wildIds.append(id);
  322. }
  323. }
  324. HashIterator it(packages);
  325. ForEach (it)
  326. {
  327. packageType *pkg = packages.getValue((const char *)it.query().getKey());
  328. if (pkg)
  329. pkg->resolveBases(this);
  330. }
  331. }
  332. void load(const char *id)
  333. {
  334. load(getPackageMapById(id, true));
  335. }
  336. virtual void gatherFileMappingForQuery(const char *queryname, IPropertyTree *fileInfo) const
  337. {
  338. Owned<IPropertyTree> query = resolveQueryAlias(querySet, queryname, true);
  339. if (!query)
  340. throw MakeStringException(PACKAGE_QUERY_NOT_FOUND, "Query %s not found", queryname);
  341. Owned<IReferencedFileList> filelist = createReferencedFileList(NULL, NULL, true);
  342. Owned<IWorkUnitFactory> wufactory = getWorkUnitFactory(NULL, NULL);
  343. Owned<IConstWorkUnit> cw = wufactory->openWorkUnit(query->queryProp("@wuid"), false);
  344. const IHpccPackage *pkg = matchPackage(query->queryProp("@id"));
  345. filelist->addFilesFromQuery(cw, pkg);
  346. Owned<IReferencedFileIterator> refFiles = filelist->getFiles();
  347. ForEach(*refFiles)
  348. {
  349. IReferencedFile &rf = refFiles->query();
  350. if (!(rf.getFlags() & RefFileInPackage))
  351. fileInfo->addProp("File", rf.getLogicalName());
  352. else
  353. {
  354. Owned<ISimpleSuperFileEnquiry> ssfe = pkg->resolveSuperFile(rf.getLogicalName());
  355. if (ssfe && ssfe->numSubFiles()>0)
  356. {
  357. IPropertyTree *superInfo = fileInfo->addPropTree("SuperFile", createPTree());
  358. superInfo->setProp("@name", rf.getLogicalName());
  359. unsigned count = ssfe->numSubFiles();
  360. while (count--)
  361. {
  362. StringBuffer subfile;
  363. ssfe->getSubFileName(count, subfile);
  364. superInfo->addProp("SubFile", subfile.str());
  365. }
  366. }
  367. }
  368. }
  369. }
  370. virtual bool validate(StringArray &queriesToCheck, StringArray &warn, StringArray &err,
  371. StringArray &unmatchedQueries, StringArray &unusedPackages, StringArray &unmatchedFiles) const
  372. {
  373. bool isValid = true;
  374. MapStringTo<bool> referencedPackages;
  375. Owned<IPropertyTree> qs = getQueryRegistry(querySet, true);
  376. if (!qs)
  377. throw MakeStringException(PACKAGE_TARGET_NOT_FOUND, "Target %s not found", querySet.sget());
  378. HashIterator it(packages);
  379. ForEach (it)
  380. {
  381. const char *packageId = (const char *)it.query().getKey();
  382. packageType *pkg = packages.getValue(packageId);
  383. if (!pkg)
  384. continue;
  385. if (!pkg->validate(warn, err))
  386. isValid = false;
  387. Owned<IPropertyTreeIterator> baseNodes = pkg->queryTree()->getElements("Base");
  388. ForEach(*baseNodes)
  389. {
  390. const char *baseId = baseNodes->query().queryProp("@id");
  391. if (!baseId || !*baseId)
  392. {
  393. VStringBuffer msg("Package '%s' contains Base element with no id attribute", packageId);
  394. err.append(msg.str());
  395. continue;
  396. }
  397. if (!referencedPackages.getValue(baseId))
  398. referencedPackages.setValue(baseId, true);
  399. }
  400. }
  401. Owned<IPropertyTree> tempQuerySet=createPTree();
  402. Owned<IPropertyTreeIterator> queries;
  403. if (queriesToCheck.length())
  404. {
  405. ForEachItemIn(i, queriesToCheck)
  406. {
  407. VStringBuffer xpath("Query[@id='%s']", queriesToCheck.item(i));
  408. Owned<IPropertyTree> queryEntry = qs->getPropTree(xpath);
  409. if (queryEntry)
  410. tempQuerySet->addPropTree("Query", queryEntry.getClear());
  411. else
  412. {
  413. VStringBuffer msg("Query %s not found in %s queryset", queriesToCheck.item(i), querySet.sget());
  414. err.append(msg);
  415. }
  416. }
  417. queries.setown(tempQuerySet->getElements("Query"));
  418. }
  419. else
  420. queries.setown(qs->getElements("Query"));
  421. if (!queries->first())
  422. {
  423. warn.append("No Queries found");
  424. return isValid;
  425. }
  426. Owned<IWorkUnitFactory> wufactory = getWorkUnitFactory(NULL, NULL);
  427. ForEach(*queries)
  428. {
  429. const char *queryid = queries->query().queryProp("@id");
  430. if (queryid && *queryid)
  431. {
  432. Owned<IReferencedFileList> filelist = createReferencedFileList(NULL, NULL, true);
  433. Owned<IConstWorkUnit> cw = wufactory->openWorkUnit(queries->query().queryProp("@wuid"), false);
  434. StringArray libnames, unresolvedLibs;
  435. gatherLibraryNames(libnames, unresolvedLibs, *wufactory, *cw, qs);
  436. PointerIArrayOf<IHpccPackage> libraries;
  437. ForEachItemIn(libitem, libnames)
  438. {
  439. const char *libname = libnames.item(libitem);
  440. const IHpccPackage *libpkg = matchPackage(libname);
  441. if (libpkg)
  442. libraries.append(LINK(const_cast<IHpccPackage*>(libpkg)));
  443. else
  444. unmatchedQueries.append(libname);
  445. }
  446. filelist->addFilesFromQuery(cw, this, queryid);
  447. Owned<IReferencedFileIterator> refFiles = filelist->getFiles();
  448. ForEach(*refFiles)
  449. {
  450. IReferencedFile &rf = refFiles->query();
  451. unsigned flags = rf.getFlags();
  452. if (flags & RefFileInPackage)
  453. {
  454. if (flags & RefFileSuper)
  455. {
  456. const char *pkgid = rf.queryPackageId();
  457. if (!pkgid || !*pkgid)
  458. continue;
  459. ForEachItemIn(libPkgItem, libraries)
  460. {
  461. IHpccPackage *libpkg = libraries.item(libPkgItem);
  462. const char *libpkgid = libpkg->locateSuperFile(rf.getLogicalName());
  463. if (libpkgid && !strieq(libpkgid, pkgid))
  464. {
  465. VStringBuffer msg("For query %s SuperFile %s defined in package %s redefined for library %s in package %s",
  466. queryid, rf.getLogicalName(), pkgid, libpkg->queryId(), libpkgid);
  467. warn.append(msg.str());
  468. }
  469. }
  470. }
  471. continue;
  472. }
  473. VStringBuffer fullname("%s/%s", queryid, rf.getLogicalName());
  474. unmatchedFiles.append(fullname);
  475. }
  476. const IHpccPackage *matched = matchPackage(queryid);
  477. if (matched)
  478. {
  479. const char *matchId = matched->queryTree()->queryProp("@id");
  480. if (!referencedPackages.getValue(matchId))
  481. referencedPackages.setValue(matchId, true);
  482. }
  483. else
  484. unmatchedQueries.append(queryid);
  485. }
  486. }
  487. ForEach (it)
  488. {
  489. const char *packageId = (const char *)it.query().getKey();
  490. if (!referencedPackages.getValue(packageId))
  491. unusedPackages.append(packageId);
  492. }
  493. return isValid;
  494. }
  495. };
  496. typedef CPackageMapOf<CPackageNode, IHpccPackage> CHpccPackageMap;
  497. //================================================================================================
  498. // CHpccPackageSet - an implementation of IHpccPackageSet
  499. //================================================================================================
  500. class WORKUNIT_API CHpccPackageSet : public CInterface, implements IHpccPackageSet
  501. {
  502. IArrayOf<CHpccPackageMap> packageMaps;
  503. StringAttr process;
  504. public:
  505. IMPLEMENT_IINTERFACE;
  506. CHpccPackageSet(const char *_process);
  507. void load(IPropertyTree *xml);
  508. virtual const IHpccPackageMap *queryActiveMap(const char *queryset) const;
  509. };
  510. #endif