wuwebview.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  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 append(const char *value)
  55. {
  56. assertex(!finalized);
  57. buffer.append(value);
  58. }
  59. void appendSchemaResource(IPropertyTree &res, ILoadedDllEntry &loadedDll)
  60. {
  61. if (res.getPropInt("@seq", -1)>=0 && res.hasProp("@id"))
  62. {
  63. int id = res.getPropInt("@id");
  64. size32_t len = 0;
  65. const void *data = NULL;
  66. if (loadedDll.getResource(len, data, "RESULT_XSD", (unsigned) id) && len>0)
  67. {
  68. buffer.append("<XmlSchema name=\"").append(res.queryProp("@name")).append("\">");
  69. if (res.getPropBool("@compressed"))
  70. {
  71. StringBuffer decompressed;
  72. decompressResource(len, data, decompressed);
  73. buffer.append(decompressed.str());
  74. }
  75. else
  76. buffer.append(len, (const char *)data);
  77. buffer.append("</XmlSchema>");
  78. }
  79. }
  80. }
  81. void appendManifestSchemas(IPropertyTree &manifest, ILoadedDllEntry &loadedDll)
  82. {
  83. assertex(!finalized);
  84. Owned<IPropertyTreeIterator> iter = manifest.getElements("Resource[@type='RESULT_XSD']");
  85. ForEach(*iter)
  86. appendSchemaResource(iter->query(), loadedDll);
  87. }
  88. void appendManifestResultSchema(IPropertyTree &manifest, const char *resultname, ILoadedDllEntry &loadedDll)
  89. {
  90. assertex(!finalized);
  91. VStringBuffer xpath("Resource[@name='%s'][@type='RESULT_XSD']", resultname);
  92. IPropertyTree *res=manifest.queryPropTree(xpath.str());
  93. if (res)
  94. appendSchemaResource(*res, loadedDll);
  95. }
  96. void appendXML(IPropertyTree *xml, const char *tag=NULL)
  97. {
  98. assertex(!finalized);
  99. if (tag)
  100. buffer.append('<').append(tag).append('>');
  101. if (xml)
  102. toXML(xml, buffer);
  103. if (tag)
  104. buffer.append("</").append(tag).append('>');
  105. }
  106. virtual void beginNode(const char *tag, offset_t startOffset)
  107. {
  108. if (streq("Dataset", tag) || streq("Exception", tag))
  109. datasetLevel++;
  110. if (datasetLevel)
  111. buffer.append('<').append(tag);
  112. }
  113. virtual void newAttribute(const char *name, const char *value)
  114. {
  115. if (datasetLevel && !streq(name, "@xmlns"))
  116. buffer.append(' ').append(name+1).append("=\"").append(value).append('\"');
  117. }
  118. virtual void beginNodeContent(const char *tag)
  119. {
  120. if (datasetLevel)
  121. buffer.append('>');
  122. }
  123. virtual void endNode(const char *tag, unsigned length, const void *value, bool binary, offset_t endOffset)
  124. {
  125. if (datasetLevel)
  126. {
  127. if (length)
  128. {
  129. if (binary)
  130. JBASE64_Encode(value, length, buffer);
  131. else
  132. buffer.append((const char *)value);
  133. }
  134. buffer.append("</").append(tag).append('>');
  135. if (streq("Dataset", tag) || streq("Exception", tag))
  136. datasetLevel--;
  137. }
  138. }
  139. StringBuffer &finalize()
  140. {
  141. if (!finalized)
  142. {
  143. buffer.appendf("</Result></Results></%sResponse>", name.sget());
  144. finalized=true;
  145. }
  146. return buffer;
  147. }
  148. const char *str()
  149. {
  150. finalize();
  151. return buffer.str();
  152. }
  153. size32_t length()
  154. {
  155. finalize();
  156. return buffer.length();
  157. }
  158. public:
  159. StringBuffer buffer;
  160. StringAttr name;
  161. bool finalized;
  162. private:
  163. int datasetLevel;
  164. };
  165. class WuWebView : public CInterface,
  166. implements IWuWebView,
  167. implements IIncludeHandler
  168. {
  169. public:
  170. IMPLEMENT_IINTERFACE;
  171. WuWebView(IConstWorkUnit &wu, const char *queryname, const char *wdir, bool mapEspDir) :
  172. manifestIncludePathsSet(false), dir(wdir), mapEspDirectories(mapEspDir)
  173. {
  174. name.set(queryname);
  175. load(wu);
  176. }
  177. WuWebView(const char *wuid, const char *queryname, const char *wdir, bool mapEspDir) :
  178. manifestIncludePathsSet(false), dir(wdir), mapEspDirectories(mapEspDir)
  179. {
  180. name.set(queryname);
  181. load(wuid);
  182. }
  183. void load(IConstWorkUnit &wu);
  184. void load(const char *wuid);
  185. IPropertyTree *ensureManifest();
  186. virtual void getResultViewNames(StringArray &names);
  187. virtual void renderResults(const char *viewName, const char *xml, StringBuffer &html);
  188. virtual void renderResults(const char *viewName, StringBuffer &html);
  189. virtual void renderSingleResult(const char *viewName, const char *resultname, StringBuffer &html);
  190. virtual void applyResultsXSLT(const char *filename, const char *xml, StringBuffer &out);
  191. virtual void applyResultsXSLT(const char *filename, StringBuffer &out);
  192. virtual StringBuffer &aggregateResources(const char *type, StringBuffer &content);
  193. void renderExpandedResults(const char *viewName, WuExpandedResultBuffer &expanded, StringBuffer &out);
  194. void appendResultSchemas(WuExpandedResultBuffer &buffer);
  195. void getResultXSLT(const char *viewName, StringBuffer &xslt, StringBuffer &abspath);
  196. void getResource(IPropertyTree *res, StringBuffer &content);
  197. void getResource(const char *name, StringBuffer &content, StringBuffer &abspath, const char *type);
  198. void calculateResourceIncludePaths();
  199. virtual bool getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly);
  200. protected:
  201. SCMStringBuffer dllname;
  202. Owned<IConstWorkUnit> wu;
  203. Owned<ILoadedDllEntry> loadedDll;
  204. Owned<IPropertyTree> manifest;
  205. SCMStringBuffer name;
  206. bool mapEspDirectories;
  207. bool manifestIncludePathsSet;
  208. StringAttr dir;
  209. StringAttr username;
  210. StringAttr pw;
  211. };
  212. IPropertyTree *WuWebView::ensureManifest()
  213. {
  214. if (!manifest)
  215. {
  216. StringBuffer xml;
  217. manifest.setown((loadedDll && getEmbeddedManifestXML(loadedDll, xml)) ? createPTreeFromXMLString(xml.str()) : createPTree());
  218. }
  219. return manifest.get();
  220. }
  221. void WuWebView::calculateResourceIncludePaths()
  222. {
  223. if (!manifestIncludePathsSet)
  224. {
  225. Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Resource[@filename]");
  226. ForEach(*iter)
  227. {
  228. StringBuffer abspath;
  229. iter->query().setProp("@res_include_path", makeAbsolutePath(iter->query().queryProp("@filename"), dir.get(), abspath).str());
  230. }
  231. manifestIncludePathsSet=true;
  232. }
  233. }
  234. bool WuWebView::getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly)
  235. {
  236. int len=strlen(includename);
  237. if (len<8)
  238. return false;
  239. //eliminate "file://"
  240. includename+=7;
  241. //eliminate extra '/' for windows absolute paths
  242. if (len>9 && includename[2]==':')
  243. includename++;
  244. StringBuffer relpath;
  245. if (mapEspDirectories && !strnicmp(includename, "/esp/", 5))
  246. relpath.append(dir.get()).append(includename+=5);
  247. else
  248. relpath.append(includename); //still correct for OS
  249. StringBuffer abspath;
  250. makeAbsolutePath(relpath.str(), abspath);
  251. IPropertyTree *res = NULL;
  252. if (manifest)
  253. {
  254. VStringBuffer xpath("Resource[@res_include_path='%s']", abspath.str());
  255. res = manifest->queryPropTree(xpath.str());
  256. }
  257. if (res)
  258. {
  259. StringBuffer xslt;
  260. getResource(res, xslt);
  261. includebuf.append(xslt.str());
  262. }
  263. else if (checkFileExists(abspath.str()))
  264. {
  265. Owned <IFile> f = createIFile(abspath.str());
  266. Owned <IFileIO> fio = f->open(IFOread);
  267. read(fio, 0, (size32_t) f->size(), includebuf);
  268. }
  269. //esp looks in two places for path starting with "xslt/"
  270. else if (mapEspDirectories && !strncmp(includename, "xslt/", 5))
  271. {
  272. StringBuffer relpath(dir.get());
  273. relpath.append("smc_").append(includename);;
  274. makeAbsolutePath(relpath.str(), abspath.clear());
  275. if (checkFileExists(abspath.str()))
  276. {
  277. Owned <IFile> f = createIFile(abspath.str());
  278. Owned <IFileIO> fio = f->open(IFOread);
  279. read(fio, 0, (size32_t) f->size(), includebuf);
  280. }
  281. }
  282. return true;
  283. }
  284. void WuWebView::getResultViewNames(StringArray &names)
  285. {
  286. Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Views/Results[@name]");
  287. ForEach(*iter)
  288. names.append(iter->query().queryProp("@name"));
  289. }
  290. void WuWebView::getResource(IPropertyTree *res, StringBuffer &content)
  291. {
  292. if (!loadedDll)
  293. return;
  294. if (res->hasProp("@id"))
  295. {
  296. int id = res->getPropInt("@id");
  297. size32_t len = 0;
  298. const void *data = NULL;
  299. if (loadedDll->getResource(len, data, res->queryProp("@type"), (unsigned) id) && len>0)
  300. {
  301. if (res->getPropBool("@compressed"))
  302. {
  303. StringBuffer decompressed;
  304. decompressResource(len, data, content);
  305. content.append(decompressed.str());
  306. }
  307. else
  308. content.append(len, (const char *)data);
  309. }
  310. }
  311. }
  312. void WuWebView::getResource(const char *name, StringBuffer &content, StringBuffer &includepath, const char *type)
  313. {
  314. VStringBuffer xpath("Resource[@name='%s']", name);
  315. if (type)
  316. xpath.append("[@type='").append(type).append("']");
  317. IPropertyTree *res = ensureManifest()->queryPropTree(xpath.str());
  318. calculateResourceIncludePaths();
  319. includepath.append(res->queryProp("@res_include_path"));
  320. if (res)
  321. getResource(res, content);
  322. }
  323. StringBuffer &WuWebView::aggregateResources(const char *type, StringBuffer &content)
  324. {
  325. VStringBuffer xpath("Resource[@type='%s']", type);
  326. Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements(xpath.str());
  327. ForEach(*iter)
  328. getResource(&iter->query(), content);
  329. return content;
  330. }
  331. void WuWebView::getResultXSLT(const char *viewName, StringBuffer &xslt, StringBuffer &abspath)
  332. {
  333. VStringBuffer xpath("Views/Results[@name='%s']/@resource", viewName);
  334. const char *resource = ensureManifest()->queryProp(xpath.str());
  335. if (resource)
  336. getResource(resource, xslt, abspath, "XSLT");
  337. }
  338. void WuWebView::renderExpandedResults(const char *viewName, WuExpandedResultBuffer &expanded, StringBuffer &out)
  339. {
  340. IPropertyTree *mf = ensureManifest();
  341. VStringBuffer xpath("Views/Results[@name='%s']", viewName);
  342. IPropertyTree *view = mf->queryPropTree(xpath.str());
  343. if (!view)
  344. throw MakeStringException(WUWEBERR_ViewResourceNotFound, "Result view %s not found", viewName);
  345. expanded.appendXML(view, "view");
  346. if (loadedDll)
  347. expanded.appendManifestSchemas(*mf, *loadedDll);
  348. expanded.finalize();
  349. const char *type=view->queryProp("@type");
  350. if (!type)
  351. throw MakeStringException(WUWEBERR_UnknownViewType, "No type defined for view %s", viewName);
  352. else if (strieq(type, "xml"))
  353. {
  354. out.swapWith(expanded.buffer);
  355. return;
  356. }
  357. else if (!strieq(type, "xslt"))
  358. throw MakeStringException(WUWEBERR_UnknownViewType, "View %s has an unknown type of %s", viewName, type);
  359. StringBuffer xslt;
  360. StringBuffer rootpath;
  361. getResultXSLT(viewName, xslt, rootpath);
  362. if (!xslt.length())
  363. throw MakeStringException(WUWEBERR_ViewResourceNotFound, "XSLT for %s view not found", viewName);
  364. Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
  365. t->setIncludeHandler(this);
  366. StringBuffer cacheId(viewName);
  367. cacheId.append('@').append(dllname.str()); //using dllname, cloned workunits can share cache entry
  368. t->setXslSource(xslt.str(), xslt.length(), cacheId.str(), rootpath.str());
  369. t->setXmlSource(expanded.buffer.str(), expanded.buffer.length());
  370. t->transform(out);
  371. }
  372. void WuWebView::renderResults(const char *viewName, const char *xml, StringBuffer &out)
  373. {
  374. WuExpandedResultBuffer buffer(name.str());
  375. buffer.appendDatasetsFromXML(xml);
  376. renderExpandedResults(viewName, buffer, out);
  377. }
  378. void WuWebView::renderResults(const char *viewName, StringBuffer &out)
  379. {
  380. WuExpandedResultBuffer buffer(name.str());
  381. buffer.appendResults(wu, username.get(), pw.get());
  382. renderExpandedResults(viewName, buffer, out);
  383. }
  384. void WuWebView::renderSingleResult(const char *viewName, const char *resultname, StringBuffer &out)
  385. {
  386. WuExpandedResultBuffer buffer(name.str());
  387. buffer.appendSingleResult(wu, resultname, username.get(), pw.get());
  388. renderExpandedResults(viewName, buffer, out);
  389. }
  390. void WuWebView::applyResultsXSLT(const char *filename, const char *xml, StringBuffer &out)
  391. {
  392. WuExpandedResultBuffer buffer(name.str());
  393. buffer.appendDatasetsFromXML(xml);
  394. if (loadedDll)
  395. buffer.appendManifestSchemas(*ensureManifest(), *loadedDll);
  396. Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
  397. t->setIncludeHandler(this);
  398. //override default behavior using filename as cache identifier, there's a chance includes are
  399. //mapped to resources and need to be distinguished in cache
  400. StringBuffer cacheId(filename);
  401. cacheId.append('@').append(dllname.str()); //cloned workunits have same dll and resources
  402. t->loadXslFromFile(filename, cacheId.str());
  403. t->setXmlSource(buffer.str(), buffer.length());
  404. t->transform(out);
  405. }
  406. void WuWebView::applyResultsXSLT(const char *filename, StringBuffer &out)
  407. {
  408. SCMStringBuffer xml;
  409. getFullWorkUnitResultsXML(username.get(), pw.get(), wu, xml, false, ExceptionSeverityInformation);
  410. applyResultsXSLT(filename, xml.str(), out);
  411. }
  412. void WuWebView::load(IConstWorkUnit &cwu)
  413. {
  414. wu.set(&cwu);
  415. if (!name.length())
  416. {
  417. wu->getJobName(name);
  418. name.s.replace(' ','_');
  419. }
  420. Owned<IConstWUQuery> q = wu->getQuery();
  421. q->getQueryDllName(dllname);
  422. try
  423. {
  424. loadedDll.setown(queryDllServer().loadDll(dllname.str(), DllLocationAnywhere));
  425. }
  426. catch(...)
  427. {
  428. DBGLOG("Failed to load %s", dllname.str());
  429. }
  430. }
  431. void WuWebView::load(const char *wuid)
  432. {
  433. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  434. Owned<IConstWorkUnit> wu = factory->openWorkUnit(wuid, false);
  435. if (!wu)
  436. throw MakeStringException(WUWEBERR_WorkUnitNotFound, "Workunit not found %s", wuid);
  437. load(*wu);
  438. }
  439. extern WUWEBVIEW_API IWuWebView *createWuWebView(IConstWorkUnit &wu, const char *queryname, const char *dir, bool mapEspDirectories)
  440. {
  441. try
  442. {
  443. return new WuWebView(wu, queryname, dir, mapEspDirectories);
  444. }
  445. catch (...)
  446. {
  447. SCMStringBuffer wuid;
  448. DBGLOG("ERROR loading workunit %s shared object.", wu.getWuid(wuid).str());
  449. }
  450. return NULL;
  451. }
  452. extern WUWEBVIEW_API IWuWebView *createWuWebView(const char *wuid, const char *queryname, const char *dir, bool mapEspDirectories)
  453. {
  454. try
  455. {
  456. return new WuWebView(wuid, queryname, dir, mapEspDirectories);
  457. }
  458. catch (...)
  459. {
  460. DBGLOG("ERROR loading workunit %s shared object.", wuid);
  461. }
  462. return NULL;
  463. }