pkgimpl.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  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 resolveLocally() const
  94. {
  95. if (isCompulsory())
  96. return false;
  97. return (node) ? node->getPropBool("@resolveLocally", false) : true; // default is false for explicit package files, but true for the default empty package
  98. }
  99. virtual bool getSysFieldTranslationEnabled() const {return false;}
  100. virtual bool getEnableFieldTranslation() const
  101. {
  102. const char *val = queryEnv("control:enableFieldTranslation");
  103. if (!val) val = queryEnv("enableFieldTranslation"); // Backward compatibility
  104. if (val)
  105. return strToBool(val);
  106. else
  107. return getSysFieldTranslationEnabled();
  108. }
  109. CPackageNode()
  110. {
  111. hash = 0;
  112. }
  113. virtual const IHpccPackage *queryRootPackage()
  114. {
  115. return NULL;
  116. }
  117. public:
  118. IMPLEMENT_IINTERFACE;
  119. CPackageNode(IPropertyTree *p);
  120. ~CPackageNode()
  121. {
  122. }
  123. // Load mergedEnvironment from local XML node
  124. void loadEnvironment();
  125. virtual const char *queryEnv(const char *varname) const
  126. {
  127. return mergedEnvironment->queryProp(varname);
  128. }
  129. virtual hash64_t queryHash() const
  130. {
  131. return hash;
  132. }
  133. virtual const IPropertyTree *queryTree() const
  134. {
  135. return node;
  136. }
  137. virtual bool validate(StringArray &warn, StringArray &err) const;
  138. };
  139. enum baseResolutionState
  140. {
  141. basesUnresolved=0,
  142. basesResolving=1,
  143. basesResolved=2
  144. };
  145. template <class TYPE>
  146. class CResolvedPackage : public TYPE
  147. {
  148. public:
  149. typedef CResolvedPackage<TYPE> self;
  150. CIArrayOf<self> bases;
  151. baseResolutionState baseResolution;
  152. CResolvedPackage<TYPE>(IPropertyTree *p) : TYPE(p), baseResolution(basesUnresolved) {}
  153. virtual aindex_t getBaseCount() const {return bases.length();}
  154. const self *getResolvedBase(aindex_t pos) const
  155. {
  156. if (pos < getBaseCount())
  157. return &bases.item(pos);
  158. return NULL;
  159. }
  160. virtual const TYPE *getBaseNode(aindex_t pos) const {return (const TYPE *) getResolvedBase(pos);}
  161. void appendBase(const IHpccPackage *base)
  162. {
  163. if (base)
  164. {
  165. const self *p = dynamic_cast<const self *>(base);
  166. bases.append(const_cast<self &>(*LINK(p))); // should really be an arrayof<const base> but that would require some fixing in jlib
  167. TYPE::hash = pkgHash64Data(sizeof(p->hash), &p->hash, TYPE::hash);
  168. TYPE::mergeEnvironment(p);
  169. }
  170. }
  171. void resolveBases(const IHpccPackageMap *packages)
  172. {
  173. if (baseResolution==basesResolved)
  174. return;
  175. if (baseResolution==basesResolving)
  176. throw MakeStringExceptionDirect(0, "PACKAGE_ERROR: circular or invalid base package definition");
  177. TYPE::loadEnvironment();
  178. if (packages)
  179. {
  180. Owned<IPropertyTreeIterator> baseIterator = TYPE::node->getElements("Base");
  181. if (!baseIterator->first())
  182. appendBase(TYPE::queryRootPackage());
  183. else
  184. {
  185. baseResolution=basesResolving;
  186. do
  187. {
  188. IPropertyTree &baseElem = baseIterator->query();
  189. const char *baseId = baseElem.queryProp("@id");
  190. if (!baseId)
  191. throw MakeStringException(PACKAGE_MISSING_ID, "PACKAGE_ERROR: base element missing id attribute");
  192. const IHpccPackage *base = packages->queryPackage(baseId);
  193. if (!base)
  194. throw MakeStringException(PACKAGE_NOT_FOUND, "PACKAGE_ERROR: base package %s not found", baseId);
  195. CResolvedPackage<TYPE> *rebase = dynamic_cast<CResolvedPackage<TYPE> *>(const_cast<IHpccPackage *>(base));
  196. rebase->resolveBases(packages);
  197. appendBase(base);
  198. }
  199. while(baseIterator->next());
  200. }
  201. baseResolution=basesResolved;
  202. }
  203. }
  204. // Search this package and any bases for an element matching xpath1, then return iterator for its children that match xpath2
  205. IPropertyTreeIterator *lookupElements(const char *xpath1, const char *xpath2) const
  206. {
  207. IPropertyTree *parentNode = TYPE::node->queryPropTree(xpath1);
  208. if (parentNode)
  209. return parentNode->getElements(xpath2);
  210. ForEachItemIn(idx, bases)
  211. {
  212. const self &basePackage = bases.item(idx);
  213. IPropertyTreeIterator *it = basePackage.lookupElements(xpath1, xpath2);
  214. if (it)
  215. return it;
  216. }
  217. return NULL;
  218. }
  219. const char *locateSuperFile(const char *superFileName) const
  220. {
  221. if (!superFileName || !*superFileName || !TYPE::node)
  222. return NULL;
  223. StringBuffer xpath;
  224. if (TYPE::hasProp(TYPE::makeSuperFileXPath(xpath, superFileName)))
  225. return TYPE::queryId();
  226. ForEachItemIn(idx, bases)
  227. {
  228. if (bases.item(idx).hasProp(xpath))
  229. return bases.item(idx).queryId();
  230. }
  231. return NULL;
  232. }
  233. bool hasSuperFile(const char *superFileName) const
  234. {
  235. if (locateSuperFile(superFileName))
  236. return true;
  237. return false;
  238. }
  239. virtual bool validate(StringArray &warn, StringArray &err) const
  240. {
  241. return TYPE::validate(warn, err);
  242. }
  243. };
  244. typedef CResolvedPackage<CPackageNode> CHpccPackage;
  245. //================================================================================================
  246. // CPackageMap - an implementation of IPackageMap using a string map
  247. //================================================================================================
  248. template <class TYPE, class IFACE>
  249. class CPackageMapOf : public CInterface, implements IHpccPackageMap
  250. {
  251. public:
  252. typedef CResolvedPackage<TYPE> packageType;
  253. MapStringToMyClassViaBase<packageType, IFACE> packages;
  254. StringAttr packageId;
  255. StringAttr querySet;
  256. bool active;
  257. bool compulsory;
  258. StringArray wildMatches, wildIds;
  259. public:
  260. IMPLEMENT_IINTERFACE;
  261. CPackageMapOf(const char *_packageId, const char *_querySet, bool _active)
  262. : packageId(_packageId), querySet(_querySet), active(_active), packages(true), compulsory(false)
  263. {
  264. }
  265. // IPackageMap interface
  266. virtual bool isActive() const
  267. {
  268. return active;
  269. }
  270. virtual const packageType *queryResolvedPackage(const char *name) const
  271. {
  272. return name ? packages.getValue(name) : NULL;
  273. }
  274. virtual const IHpccPackage *queryPackage(const char *name) const
  275. {
  276. return name ? (IFACE*)packages.getValue(name) : NULL;
  277. }
  278. virtual const char *queryPackageId() const
  279. {
  280. return packageId;
  281. }
  282. virtual const packageType *matchResolvedPackage(const char *name) const
  283. {
  284. if (name && *name)
  285. {
  286. const packageType *pkg = queryResolvedPackage(name);
  287. if (pkg)
  288. return pkg;
  289. const char *tail = name + strlen(name)-1;
  290. while (tail>name && isdigit(*tail))
  291. tail--;
  292. if (*tail=='.' && tail>name)
  293. {
  294. StringAttr notail(name, tail-name);
  295. pkg = queryResolvedPackage(notail);
  296. if (pkg)
  297. return pkg;
  298. }
  299. ForEachItemIn(idx, wildMatches)
  300. {
  301. if (WildMatch(name, wildMatches.item(idx), true))
  302. return queryResolvedPackage(wildIds.item(idx));
  303. }
  304. }
  305. return NULL;
  306. }
  307. const IHpccPackage *matchPackage(const char *name) const
  308. {
  309. return (IFACE *) matchResolvedPackage(name);
  310. }
  311. void load(IPropertyTree *xml)
  312. {
  313. if (!xml)
  314. return;
  315. compulsory = xml->getPropBool("@compulsory");
  316. Owned<IPropertyTreeIterator> allpackages = xml->getElements("Package");
  317. ForEach(*allpackages)
  318. {
  319. IPropertyTree &packageTree = allpackages->query();
  320. const char *id = packageTree.queryProp("@id");
  321. if (!id || !*id)
  322. throw MakeStringException(PACKAGE_MISSING_ID, "Invalid package map - Package element missing id attribute");
  323. Owned<packageType> package = new packageType(&packageTree);
  324. packages.setValue(id, package.get());
  325. const char *queries = packageTree.queryProp("@queries");
  326. if (queries && *queries)
  327. {
  328. wildMatches.append(queries);
  329. wildIds.append(id);
  330. }
  331. }
  332. HashIterator it(packages);
  333. ForEach (it)
  334. {
  335. packageType *pkg = packages.getValue((const char *)it.query().getKey());
  336. if (pkg)
  337. pkg->resolveBases(this);
  338. }
  339. }
  340. void load(const char *id)
  341. {
  342. Owned<IPropertyTree> xml = getPackageMapById(id, true);
  343. load(xml);
  344. }
  345. virtual void gatherFileMappingForQuery(const char *queryname, IPropertyTree *fileInfo) const
  346. {
  347. Owned<IPropertyTree> query = resolveQueryAlias(querySet, queryname, true);
  348. if (!query)
  349. throw MakeStringException(PACKAGE_QUERY_NOT_FOUND, "Query %s not found", queryname);
  350. Owned<IReferencedFileList> filelist = createReferencedFileList(NULL, NULL, true);
  351. Owned<IWorkUnitFactory> wufactory = getWorkUnitFactory(NULL, NULL);
  352. Owned<IConstWorkUnit> cw = wufactory->openWorkUnit(query->queryProp("@wuid"), false);
  353. const IHpccPackage *pkg = matchPackage(query->queryProp("@id"));
  354. filelist->addFilesFromQuery(cw, pkg);
  355. Owned<IReferencedFileIterator> refFiles = filelist->getFiles();
  356. ForEach(*refFiles)
  357. {
  358. IReferencedFile &rf = refFiles->query();
  359. if (!(rf.getFlags() & RefFileInPackage))
  360. fileInfo->addProp("File", rf.getLogicalName());
  361. else
  362. {
  363. Owned<ISimpleSuperFileEnquiry> ssfe = pkg->resolveSuperFile(rf.getLogicalName());
  364. if (ssfe && ssfe->numSubFiles()>0)
  365. {
  366. IPropertyTree *superInfo = fileInfo->addPropTree("SuperFile", createPTree());
  367. superInfo->setProp("@name", rf.getLogicalName());
  368. unsigned count = ssfe->numSubFiles();
  369. while (count--)
  370. {
  371. StringBuffer subfile;
  372. ssfe->getSubFileName(count, subfile);
  373. superInfo->addProp("SubFile", subfile.str());
  374. }
  375. }
  376. }
  377. }
  378. }
  379. virtual bool validate(StringArray &queriesToCheck, StringArray &warn, StringArray &err,
  380. StringArray &unmatchedQueries, StringArray &unusedPackages, StringArray &unmatchedFiles) const
  381. {
  382. bool isValid = true;
  383. MapStringTo<bool> referencedPackages;
  384. Owned<IPropertyTree> qs = getQueryRegistry(querySet, true);
  385. if (!qs)
  386. throw MakeStringException(PACKAGE_TARGET_NOT_FOUND, "Target %s not found", querySet.sget());
  387. HashIterator it(packages);
  388. ForEach (it)
  389. {
  390. const char *packageId = (const char *)it.query().getKey();
  391. packageType *pkg = packages.getValue(packageId);
  392. if (!pkg)
  393. continue;
  394. if (!pkg->validate(warn, err))
  395. isValid = false;
  396. Owned<IPropertyTreeIterator> baseNodes = pkg->queryTree()->getElements("Base");
  397. ForEach(*baseNodes)
  398. {
  399. const char *baseId = baseNodes->query().queryProp("@id");
  400. if (!baseId || !*baseId)
  401. {
  402. VStringBuffer msg("Package '%s' contains Base element with no id attribute", packageId);
  403. err.append(msg.str());
  404. continue;
  405. }
  406. if (!referencedPackages.getValue(baseId))
  407. referencedPackages.setValue(baseId, true);
  408. }
  409. }
  410. Owned<IPropertyTree> tempQuerySet=createPTree();
  411. Owned<IPropertyTreeIterator> queries;
  412. if (queriesToCheck.length())
  413. {
  414. ForEachItemIn(i, queriesToCheck)
  415. {
  416. VStringBuffer xpath("Query[@id='%s']", queriesToCheck.item(i));
  417. Owned<IPropertyTree> queryEntry = qs->getPropTree(xpath);
  418. if (queryEntry)
  419. tempQuerySet->addPropTree("Query", queryEntry.getClear());
  420. else
  421. {
  422. VStringBuffer msg("Query %s not found in %s queryset", queriesToCheck.item(i), querySet.sget());
  423. err.append(msg);
  424. }
  425. }
  426. queries.setown(tempQuerySet->getElements("Query"));
  427. }
  428. else
  429. queries.setown(qs->getElements("Query"));
  430. if (!queries->first())
  431. {
  432. warn.append("No Queries found");
  433. return isValid;
  434. }
  435. Owned<IWorkUnitFactory> wufactory = getWorkUnitFactory(NULL, NULL);
  436. ForEach(*queries)
  437. {
  438. const char *queryid = queries->query().queryProp("@id");
  439. if (queryid && *queryid)
  440. {
  441. Owned<IReferencedFileList> filelist = createReferencedFileList(NULL, NULL, true);
  442. Owned<IConstWorkUnit> cw = wufactory->openWorkUnit(queries->query().queryProp("@wuid"), false);
  443. StringArray libnames, unresolvedLibs;
  444. gatherLibraryNames(libnames, unresolvedLibs, *wufactory, *cw, qs);
  445. PointerIArrayOf<IHpccPackage> libraries;
  446. ForEachItemIn(libitem, libnames)
  447. {
  448. const char *libname = libnames.item(libitem);
  449. const IHpccPackage *libpkg = matchPackage(libname);
  450. if (libpkg)
  451. libraries.append(LINK(const_cast<IHpccPackage*>(libpkg)));
  452. else
  453. unmatchedQueries.append(libname);
  454. }
  455. filelist->addFilesFromQuery(cw, this, queryid);
  456. Owned<IReferencedFileIterator> refFiles = filelist->getFiles();
  457. ForEach(*refFiles)
  458. {
  459. IReferencedFile &rf = refFiles->query();
  460. unsigned flags = rf.getFlags();
  461. if (flags & RefFileInPackage)
  462. {
  463. if (flags & RefFileSuper)
  464. {
  465. const char *pkgid = rf.queryPackageId();
  466. if (!pkgid || !*pkgid)
  467. continue;
  468. ForEachItemIn(libPkgItem, libraries)
  469. {
  470. IHpccPackage *libpkg = libraries.item(libPkgItem);
  471. const char *libpkgid = libpkg->locateSuperFile(rf.getLogicalName());
  472. if (libpkgid && !strieq(libpkgid, pkgid))
  473. {
  474. VStringBuffer msg("For query %s SuperFile %s defined in package %s redefined for library %s in package %s",
  475. queryid, rf.getLogicalName(), pkgid, libpkg->queryId(), libpkgid);
  476. warn.append(msg.str());
  477. }
  478. }
  479. }
  480. continue;
  481. }
  482. VStringBuffer fullname("%s/%s", queryid, rf.getLogicalName());
  483. unmatchedFiles.append(fullname);
  484. }
  485. const IHpccPackage *matched = matchPackage(queryid);
  486. if (matched)
  487. {
  488. const char *matchId = matched->queryTree()->queryProp("@id");
  489. if (!referencedPackages.getValue(matchId))
  490. referencedPackages.setValue(matchId, true);
  491. }
  492. else
  493. unmatchedQueries.append(queryid);
  494. }
  495. }
  496. ForEach (it)
  497. {
  498. const char *packageId = (const char *)it.query().getKey();
  499. if (!referencedPackages.getValue(packageId))
  500. unusedPackages.append(packageId);
  501. }
  502. return isValid;
  503. }
  504. };
  505. typedef CPackageMapOf<CPackageNode, IHpccPackage> CHpccPackageMap;
  506. //================================================================================================
  507. // CHpccPackageSet - an implementation of IHpccPackageSet
  508. //================================================================================================
  509. class WORKUNIT_API CHpccPackageSet : public CInterface, implements IHpccPackageSet
  510. {
  511. IArrayOf<CHpccPackageMap> packageMaps;
  512. StringAttr process;
  513. public:
  514. IMPLEMENT_IINTERFACE;
  515. CHpccPackageSet(const char *_process);
  516. void load(IPropertyTree *xml);
  517. virtual const IHpccPackageMap *queryActiveMap(const char *queryset) const;
  518. };
  519. #endif