wuwebview.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "jlib.hpp"
  15. #include "jexcept.hpp"
  16. #include "jptree.hpp"
  17. #include "workunit.hpp"
  18. #include "dllserver.hpp"
  19. #include "thorplugin.hpp"
  20. #include "xslprocessor.hpp"
  21. #include "fileview.hpp"
  22. #include "wuwebview.hpp"
  23. #include "wuweberror.hpp"
  24. class WuExpandedResultBuffer : public CInterface, implements IPTreeNotifyEvent
  25. {
  26. public:
  27. IMPLEMENT_IINTERFACE;
  28. WuExpandedResultBuffer(const char *queryname) : name(queryname), datasetLevel(0), finalized(false)
  29. {
  30. buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><");
  31. if (queryname)
  32. buffer.append(queryname);
  33. buffer.append("Response><Results><Result>");
  34. }
  35. void appendResults(IConstWorkUnit *wu, const char *username, const char *pw)
  36. {
  37. StringBufferAdaptor resultXML(buffer);
  38. getFullWorkUnitResultsXML(username, pw, wu, resultXML, false, ExceptionSeverityError, true);
  39. }
  40. void appendSingleResult(IConstWorkUnit *wu, const char *resultname, const char *username, const char *pw)
  41. {
  42. SCMStringBuffer wuid;
  43. StringBufferAdaptor resultXML(buffer);
  44. Owned<IResultSetFactory> factory = getResultSetFactory(username, pw);
  45. Owned<INewResultSet> nr = factory->createNewResultSet(wu->getWuid(wuid).str(), 0, resultname);
  46. getResultXml(resultXML, nr.get(), resultname, 0, 0, NULL);
  47. }
  48. void appendDatasetsFromXML(const char *xml)
  49. {
  50. assertex(!finalized);
  51. Owned<IPullXMLReader> reader = createPullXMLStringReader(xml, *this);
  52. reader->load();
  53. }
  54. void appendDatasetXML(const char *xml)
  55. {
  56. assertex(!finalized);
  57. buffer.append(xml);
  58. }
  59. void append(const char *value)
  60. {
  61. assertex(!finalized);
  62. buffer.append(value);
  63. }
  64. void appendSchemaResource(IPropertyTree &res, ILoadedDllEntry &loadedDll)
  65. {
  66. if (res.getPropInt("@seq", -1)>=0 && res.hasProp("@id"))
  67. {
  68. int id = res.getPropInt("@id");
  69. size32_t len = 0;
  70. const void *data = NULL;
  71. if (loadedDll.getResource(len, data, "RESULT_XSD", (unsigned) id) && len>0)
  72. {
  73. buffer.append("<XmlSchema name=\"").append(res.queryProp("@name")).append("\">");
  74. if (res.getPropBool("@compressed"))
  75. {
  76. StringBuffer decompressed;
  77. decompressResource(len, data, decompressed);
  78. buffer.append(decompressed.str());
  79. }
  80. else
  81. buffer.append(len, (const char *)data);
  82. buffer.append("</XmlSchema>");
  83. }
  84. }
  85. }
  86. void appendManifestSchemas(IPropertyTree &manifest, ILoadedDllEntry &loadedDll)
  87. {
  88. assertex(!finalized);
  89. Owned<IPropertyTreeIterator> iter = manifest.getElements("Resource[@type='RESULT_XSD']");
  90. ForEach(*iter)
  91. appendSchemaResource(iter->query(), loadedDll);
  92. }
  93. void appendManifestResultSchema(IPropertyTree &manifest, const char *resultname, ILoadedDllEntry &loadedDll)
  94. {
  95. assertex(!finalized);
  96. VStringBuffer xpath("Resource[@name='%s'][@type='RESULT_XSD']", resultname);
  97. IPropertyTree *res=manifest.queryPropTree(xpath.str());
  98. if (res)
  99. appendSchemaResource(*res, loadedDll);
  100. }
  101. virtual void beginNode(const char *tag, offset_t startOffset)
  102. {
  103. if (streq("Dataset", tag))
  104. datasetLevel++;
  105. if (datasetLevel)
  106. buffer.append('<').append(tag);
  107. }
  108. virtual void newAttribute(const char *name, const char *value)
  109. {
  110. if (datasetLevel && !streq(name, "@xmlns"))
  111. buffer.append(' ').append(name+1).append("=\"").append(value).append('\"');
  112. }
  113. virtual void beginNodeContent(const char *tag)
  114. {
  115. if (datasetLevel)
  116. buffer.append('>');
  117. }
  118. virtual void endNode(const char *tag, unsigned length, const void *value, bool binary, offset_t endOffset)
  119. {
  120. if (datasetLevel)
  121. {
  122. if (length)
  123. {
  124. if (binary)
  125. JBASE64_Encode(value, length, buffer);
  126. else
  127. buffer.append((const char *)value);
  128. }
  129. buffer.append("</").append(tag).append('>');
  130. if (streq("Dataset", tag))
  131. datasetLevel--;
  132. }
  133. }
  134. StringBuffer &finalize()
  135. {
  136. if (!finalized)
  137. {
  138. buffer.appendf("</Result></Results></%sResponse>", name.sget());
  139. finalized=true;
  140. }
  141. return buffer;
  142. }
  143. const char *str()
  144. {
  145. finalize();
  146. return buffer.str();
  147. }
  148. size32_t length()
  149. {
  150. finalize();
  151. return buffer.length();
  152. }
  153. public:
  154. StringBuffer buffer;
  155. StringAttr name;
  156. bool finalized;
  157. private:
  158. int datasetLevel;
  159. };
  160. class WuWebView : public CInterface,
  161. implements IWuWebView,
  162. implements IIncludeHandler
  163. {
  164. public:
  165. IMPLEMENT_IINTERFACE;
  166. WuWebView(IConstWorkUnit &wu, const char *queryname, const char *wdir, bool mapEspDir) :
  167. manifestIncludePathsSet(false), dir(wdir), mapEspDirectories(mapEspDir)
  168. {
  169. name.set(queryname);
  170. load(wu);
  171. }
  172. WuWebView(const char *wuid, const char *queryname, const char *wdir, bool mapEspDir) :
  173. manifestIncludePathsSet(false), dir(wdir), mapEspDirectories(mapEspDir)
  174. {
  175. name.set(queryname);
  176. load(wuid);
  177. }
  178. void load(IConstWorkUnit &wu);
  179. void load(const char *wuid);
  180. IPropertyTree *ensureManifest();
  181. virtual void getResultViewNames(StringArray &names);
  182. virtual void renderResults(const char *viewName, const char *xml, StringBuffer &html);
  183. virtual void renderResults(const char *viewName, StringBuffer &html);
  184. virtual void renderSingleResult(const char *viewName, const char *resultname, StringBuffer &html);
  185. virtual void applyResultsXSLT(const char *filename, const char *xml, StringBuffer &out);
  186. virtual void applyResultsXSLT(const char *filename, StringBuffer &out);
  187. virtual StringBuffer &aggregateResources(const char *type, StringBuffer &content);
  188. void renderExpandedResults(const char *viewName, const StringBuffer &expanded, StringBuffer &out);
  189. void appendResultSchemas(WuExpandedResultBuffer &buffer);
  190. void getResultXSLT(const char *viewName, StringBuffer &xslt, StringBuffer &abspath);
  191. void getResource(IPropertyTree *res, StringBuffer &content);
  192. void getResource(const char *name, StringBuffer &content, StringBuffer &abspath);
  193. void calculateResourceIncludePaths();
  194. virtual bool getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly);
  195. protected:
  196. SCMStringBuffer dllname;
  197. Owned<IConstWorkUnit> wu;
  198. Owned<ILoadedDllEntry> loadedDll;
  199. Owned<IPropertyTree> manifest;
  200. SCMStringBuffer name;
  201. bool mapEspDirectories;
  202. bool manifestIncludePathsSet;
  203. StringAttr dir;
  204. StringAttr username;
  205. StringAttr pw;
  206. };
  207. IPropertyTree *WuWebView::ensureManifest()
  208. {
  209. if (!manifest)
  210. {
  211. StringBuffer xml;
  212. manifest.setown(getEmbeddedManifestXML(loadedDll, xml) ? createPTreeFromXMLString(xml.str()) : createPTree());
  213. }
  214. return manifest.get();
  215. }
  216. void WuWebView::calculateResourceIncludePaths()
  217. {
  218. if (!manifestIncludePathsSet)
  219. {
  220. Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Resource[@filename]");
  221. ForEach(*iter)
  222. {
  223. StringBuffer abspath;
  224. iter->query().setProp("@res_include_path", makeAbsolutePath(iter->query().queryProp("@filename"), dir.get(), abspath).str());
  225. }
  226. manifestIncludePathsSet=true;
  227. }
  228. }
  229. bool WuWebView::getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly)
  230. {
  231. int len=strlen(includename);
  232. if (len<8)
  233. return false;
  234. //eliminate "file://"
  235. includename+=7;
  236. //eliminate extra '/' for windows absolute paths
  237. if (len>9 && includename[2]==':')
  238. includename++;
  239. StringBuffer relpath;
  240. if (mapEspDirectories && !strnicmp(includename, "/esp/", 5))
  241. relpath.append(dir.get()).append(includename+=5);
  242. else
  243. relpath.append(includename); //still correct for OS
  244. StringBuffer abspath;
  245. makeAbsolutePath(relpath.str(), abspath);
  246. VStringBuffer xpath("Resource[@res_include_path='%s']", abspath.str());
  247. IPropertyTree *res = manifest->queryPropTree(xpath.str());
  248. if (res)
  249. {
  250. StringBuffer xslt;
  251. getResource(res, xslt);
  252. includebuf.append(xslt.str());
  253. }
  254. else if (checkFileExists(abspath.str()))
  255. {
  256. Owned <IFile> f = createIFile(abspath.str());
  257. Owned <IFileIO> fio = f->open(IFOread);
  258. read(fio, 0, (size32_t) f->size(), includebuf);
  259. }
  260. //esp looks in two places for path starting with "xslt/"
  261. else if (mapEspDirectories && !strncmp(includename, "xslt/", 5))
  262. {
  263. StringBuffer relpath(dir.get());
  264. relpath.append("smc_").append(includename);;
  265. makeAbsolutePath(relpath.str(), abspath.clear());
  266. if (checkFileExists(abspath.str()))
  267. {
  268. Owned <IFile> f = createIFile(abspath.str());
  269. Owned <IFileIO> fio = f->open(IFOread);
  270. read(fio, 0, (size32_t) f->size(), includebuf);
  271. }
  272. }
  273. return true;
  274. }
  275. void WuWebView::getResultViewNames(StringArray &names)
  276. {
  277. Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Views/Results[@name]");
  278. ForEach(*iter)
  279. names.append(iter->query().queryProp("@name"));
  280. }
  281. void WuWebView::getResource(IPropertyTree *res, StringBuffer &content)
  282. {
  283. if (res->hasProp("@id"))
  284. {
  285. int id = res->getPropInt("@id");
  286. size32_t len = 0;
  287. const void *data = NULL;
  288. if (loadedDll->getResource(len, data, res->queryProp("@type"), (unsigned) id) && len>0)
  289. {
  290. if (res->getPropBool("@compressed"))
  291. {
  292. StringBuffer decompressed;
  293. decompressResource(len, data, content);
  294. content.append(decompressed.str());
  295. }
  296. else
  297. content.append(len, (const char *)data);
  298. }
  299. }
  300. }
  301. void WuWebView::getResource(const char *name, StringBuffer &content, StringBuffer &includepath)
  302. {
  303. VStringBuffer xpath("Resource[@name='%s']", name);
  304. IPropertyTree *res = ensureManifest()->queryPropTree(xpath.str());
  305. calculateResourceIncludePaths();
  306. includepath.append(res->queryProp("@res_include_path"));
  307. if (res)
  308. getResource(res, content);
  309. }
  310. StringBuffer &WuWebView::aggregateResources(const char *type, StringBuffer &content)
  311. {
  312. VStringBuffer xpath("Resource[@type='%s']", type);
  313. Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements(xpath.str());
  314. ForEach(*iter)
  315. getResource(&iter->query(), content);
  316. return content;
  317. }
  318. void WuWebView::getResultXSLT(const char *viewName, StringBuffer &xslt, StringBuffer &abspath)
  319. {
  320. VStringBuffer xpath("Views/Results[@name='%s']/@resource", viewName);
  321. const char *resource = ensureManifest()->queryProp(xpath.str());
  322. if (resource)
  323. getResource(resource, xslt, abspath);
  324. }
  325. void WuWebView::renderExpandedResults(const char *viewName, const StringBuffer &expanded, StringBuffer &out)
  326. {
  327. StringBuffer xslt;
  328. StringBuffer rootpath;
  329. getResultXSLT(viewName, xslt, rootpath);
  330. if (!xslt.length())
  331. throw MakeStringException(WUWEBERR_ViewResourceNotFound, "Result view %s not found", viewName);
  332. Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
  333. t->setIncludeHandler(this);
  334. StringBuffer cacheId(viewName);
  335. cacheId.append('@').append(dllname.str()); //using dllname, cloned workunits can share cache entry
  336. t->setXslSource(xslt.str(), xslt.length(), cacheId.str(), rootpath.str());
  337. t->setXmlSource(expanded.str(), expanded.length());
  338. t->transform(out);
  339. }
  340. void WuWebView::renderResults(const char *viewName, const char *xml, StringBuffer &out)
  341. {
  342. WuExpandedResultBuffer buffer(name.str());
  343. buffer.appendDatasetsFromXML(xml);
  344. buffer.appendManifestSchemas(*ensureManifest(), *loadedDll);
  345. renderExpandedResults(viewName, buffer.finalize(), out);
  346. }
  347. void WuWebView::renderResults(const char *viewName, StringBuffer &out)
  348. {
  349. WuExpandedResultBuffer buffer(name.str());
  350. buffer.appendResults(wu, username.get(), pw.get());
  351. buffer.appendManifestSchemas(*ensureManifest(), *loadedDll);
  352. renderExpandedResults(viewName, buffer.finalize(), out);
  353. }
  354. void WuWebView::renderSingleResult(const char *viewName, const char *resultname, StringBuffer &out)
  355. {
  356. WuExpandedResultBuffer buffer(name.str());
  357. buffer.appendManifestResultSchema(*ensureManifest(), resultname, *loadedDll);
  358. buffer.appendSingleResult(wu, resultname, username.get(), pw.get());
  359. renderExpandedResults(viewName, buffer.finalize(), out);
  360. }
  361. void WuWebView::applyResultsXSLT(const char *filename, const char *xml, StringBuffer &out)
  362. {
  363. WuExpandedResultBuffer buffer(name.str());
  364. buffer.appendDatasetsFromXML(xml);
  365. buffer.appendManifestSchemas(*ensureManifest(), *loadedDll);
  366. Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
  367. t->setIncludeHandler(this);
  368. //override default behavior using filename as cache identifier, there's a chance includes are
  369. //mapped to resources and need to be distinguished in cache
  370. StringBuffer cacheId(filename);
  371. cacheId.append('@').append(dllname.str()); //cloned workunits have same dll and resources
  372. t->loadXslFromFile(filename, cacheId.str());
  373. t->setXmlSource(buffer.str(), buffer.length());
  374. t->transform(out);
  375. }
  376. void WuWebView::applyResultsXSLT(const char *filename, StringBuffer &out)
  377. {
  378. SCMStringBuffer xml;
  379. getFullWorkUnitResultsXML(username.get(), pw.get(), wu, xml, false, ExceptionSeverityError);
  380. applyResultsXSLT(filename, xml.str(), out);
  381. }
  382. void WuWebView::load(IConstWorkUnit &cwu)
  383. {
  384. wu.set(&cwu);
  385. if (!name.length())
  386. {
  387. wu->getJobName(name);
  388. name.s.replace(' ','_');
  389. }
  390. Owned<IConstWUQuery> q = wu->getQuery();
  391. q->getQueryDllName(dllname);
  392. loadedDll.setown(queryDllServer().loadDll(dllname.str(), DllLocationAnywhere));
  393. }
  394. void WuWebView::load(const char *wuid)
  395. {
  396. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  397. Owned<IConstWorkUnit> wu = factory->openWorkUnit(wuid, false);
  398. if (!wu)
  399. throw MakeStringException(WUWEBERR_WorkUnitNotFound, "Workunit not found %s", wuid);
  400. load(*wu);
  401. }
  402. extern WUWEBVIEW_API IWuWebView *createWuWebView(IConstWorkUnit &wu, const char *queryname, const char *dir, bool mapEspDirectories)
  403. {
  404. try
  405. {
  406. return new WuWebView(wu, queryname, dir, mapEspDirectories);
  407. }
  408. catch (...)
  409. {
  410. SCMStringBuffer wuid;
  411. DBGLOG("ERROR loading workunit %s shared object.", wu.getWuid(wuid).str());
  412. }
  413. return NULL;
  414. }
  415. extern WUWEBVIEW_API IWuWebView *createWuWebView(const char *wuid, const char *queryname, const char *dir, bool mapEspDirectories)
  416. {
  417. try
  418. {
  419. return new WuWebView(wuid, queryname, dir, mapEspDirectories);
  420. }
  421. catch (...)
  422. {
  423. DBGLOG("ERROR loading workunit %s shared object.", wuid);
  424. }
  425. return NULL;
  426. }