123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /*##############################################################################
- Copyright (C) 2011 HPCC Systems.
- All rights reserved. This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ############################################################################## */
- #include "jlib.hpp"
- #include "jexcept.hpp"
- #include "jptree.hpp"
- #include "workunit.hpp"
- #include "dllserver.hpp"
- #include "thorplugin.hpp"
- #include "xslprocessor.hpp"
- #include "fileview.hpp"
- #include "wuwebview.hpp"
- #include "wuweberror.hpp"
- class WuExpandedResultBuffer : public CInterface, implements IPTreeNotifyEvent
- {
- public:
- IMPLEMENT_IINTERFACE;
- WuExpandedResultBuffer(const char *queryname) : name(queryname), datasetLevel(0), finalized(false)
- {
- buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><");
- if (queryname)
- buffer.append(queryname);
- buffer.append("Response><Results><Result>");
- }
- void appendResults(IConstWorkUnit *wu, const char *username, const char *pw)
- {
- StringBufferAdaptor resultXML(buffer);
- getFullWorkUnitResultsXML(username, pw, wu, resultXML, false, ExceptionSeverityError, true);
- }
- void appendSingleResult(IConstWorkUnit *wu, const char *resultname, const char *username, const char *pw)
- {
- SCMStringBuffer wuid;
- StringBufferAdaptor resultXML(buffer);
- Owned<IResultSetFactory> factory = getResultSetFactory(username, pw);
- Owned<INewResultSet> nr = factory->createNewResultSet(wu->getWuid(wuid).str(), 0, resultname);
- getResultXml(resultXML, nr.get(), resultname, 0, 0, NULL);
- }
- void appendDatasetsFromXML(const char *xml)
- {
- assertex(!finalized);
- Owned<IPullXMLReader> reader = createPullXMLStringReader(xml, *this);
- reader->load();
- }
- void append(const char *value)
- {
- assertex(!finalized);
- buffer.append(value);
- }
- void appendSchemaResource(IPropertyTree &res, ILoadedDllEntry &loadedDll)
- {
- if (res.getPropInt("@seq", -1)>=0 && res.hasProp("@id"))
- {
- int id = res.getPropInt("@id");
- size32_t len = 0;
- const void *data = NULL;
- if (loadedDll.getResource(len, data, "RESULT_XSD", (unsigned) id) && len>0)
- {
- buffer.append("<XmlSchema name=\"").append(res.queryProp("@name")).append("\">");
- if (res.getPropBool("@compressed"))
- {
- StringBuffer decompressed;
- decompressResource(len, data, decompressed);
- buffer.append(decompressed.str());
- }
- else
- buffer.append(len, (const char *)data);
- buffer.append("</XmlSchema>");
- }
- }
- }
- void appendManifestSchemas(IPropertyTree &manifest, ILoadedDllEntry &loadedDll)
- {
- assertex(!finalized);
- Owned<IPropertyTreeIterator> iter = manifest.getElements("Resource[@type='RESULT_XSD']");
- ForEach(*iter)
- appendSchemaResource(iter->query(), loadedDll);
- }
- void appendManifestResultSchema(IPropertyTree &manifest, const char *resultname, ILoadedDllEntry &loadedDll)
- {
- assertex(!finalized);
- VStringBuffer xpath("Resource[@name='%s'][@type='RESULT_XSD']", resultname);
- IPropertyTree *res=manifest.queryPropTree(xpath.str());
- if (res)
- appendSchemaResource(*res, loadedDll);
- }
- void appendXML(IPropertyTree *xml, const char *tag=NULL)
- {
- assertex(!finalized);
- if (tag)
- buffer.append('<').append(tag).append('>');
- if (xml)
- toXML(xml, buffer);
- if (tag)
- buffer.append("</").append(tag).append('>');
- }
- virtual void beginNode(const char *tag, offset_t startOffset)
- {
- if (streq("Dataset", tag) || streq("Exception", tag))
- datasetLevel++;
- if (datasetLevel)
- buffer.append('<').append(tag);
- }
- virtual void newAttribute(const char *name, const char *value)
- {
- if (datasetLevel && !streq(name, "@xmlns"))
- buffer.append(' ').append(name+1).append("=\"").append(value).append('\"');
- }
- virtual void beginNodeContent(const char *tag)
- {
- if (datasetLevel)
- buffer.append('>');
- }
- virtual void endNode(const char *tag, unsigned length, const void *value, bool binary, offset_t endOffset)
- {
- if (datasetLevel)
- {
- if (length)
- {
- if (binary)
- JBASE64_Encode(value, length, buffer);
- else
- buffer.append((const char *)value);
- }
- buffer.append("</").append(tag).append('>');
- if (streq("Dataset", tag) || streq("Exception", tag))
- datasetLevel--;
- }
- }
- StringBuffer &finalize()
- {
- if (!finalized)
- {
- buffer.appendf("</Result></Results></%sResponse>", name.sget());
- finalized=true;
- }
- return buffer;
- }
- const char *str()
- {
- finalize();
- return buffer.str();
- }
- size32_t length()
- {
- finalize();
- return buffer.length();
- }
- public:
- StringBuffer buffer;
- StringAttr name;
- bool finalized;
- private:
- int datasetLevel;
- };
- class WuWebView : public CInterface,
- implements IWuWebView,
- implements IIncludeHandler
- {
- public:
- IMPLEMENT_IINTERFACE;
- WuWebView(IConstWorkUnit &wu, const char *queryname, const char *wdir, bool mapEspDir) :
- manifestIncludePathsSet(false), dir(wdir), mapEspDirectories(mapEspDir)
- {
- name.set(queryname);
- load(wu);
- }
- WuWebView(const char *wuid, const char *queryname, const char *wdir, bool mapEspDir) :
- manifestIncludePathsSet(false), dir(wdir), mapEspDirectories(mapEspDir)
- {
- name.set(queryname);
- load(wuid);
- }
- void load(IConstWorkUnit &wu);
- void load(const char *wuid);
- IPropertyTree *ensureManifest();
- virtual void getResultViewNames(StringArray &names);
- virtual void renderResults(const char *viewName, const char *xml, StringBuffer &html);
- virtual void renderResults(const char *viewName, StringBuffer &html);
- virtual void renderSingleResult(const char *viewName, const char *resultname, StringBuffer &html);
- virtual void applyResultsXSLT(const char *filename, const char *xml, StringBuffer &out);
- virtual void applyResultsXSLT(const char *filename, StringBuffer &out);
- virtual StringBuffer &aggregateResources(const char *type, StringBuffer &content);
- void renderExpandedResults(const char *viewName, WuExpandedResultBuffer &expanded, StringBuffer &out);
- void appendResultSchemas(WuExpandedResultBuffer &buffer);
- void getResultXSLT(const char *viewName, StringBuffer &xslt, StringBuffer &abspath);
- void getResource(IPropertyTree *res, StringBuffer &content);
- void getResource(const char *name, StringBuffer &content, StringBuffer &abspath, const char *type);
- void calculateResourceIncludePaths();
- virtual bool getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly);
- protected:
- SCMStringBuffer dllname;
- Owned<IConstWorkUnit> wu;
- Owned<ILoadedDllEntry> loadedDll;
- Owned<IPropertyTree> manifest;
- SCMStringBuffer name;
- bool mapEspDirectories;
- bool manifestIncludePathsSet;
- StringAttr dir;
- StringAttr username;
- StringAttr pw;
- };
- IPropertyTree *WuWebView::ensureManifest()
- {
- if (!manifest)
- {
- StringBuffer xml;
- manifest.setown((loadedDll && getEmbeddedManifestXML(loadedDll, xml)) ? createPTreeFromXMLString(xml.str()) : createPTree());
- }
- return manifest.get();
- }
- void WuWebView::calculateResourceIncludePaths()
- {
- if (!manifestIncludePathsSet)
- {
- Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Resource[@filename]");
- ForEach(*iter)
- {
- StringBuffer abspath;
- iter->query().setProp("@res_include_path", makeAbsolutePath(iter->query().queryProp("@filename"), dir.get(), abspath).str());
- }
- manifestIncludePathsSet=true;
- }
- }
- bool WuWebView::getInclude(const char *includename, MemoryBuffer &includebuf, bool &pathOnly)
- {
- int len=strlen(includename);
- if (len<8)
- return false;
- //eliminate "file://"
- includename+=7;
- //eliminate extra '/' for windows absolute paths
- if (len>9 && includename[2]==':')
- includename++;
- StringBuffer relpath;
- if (mapEspDirectories && !strnicmp(includename, "/esp/", 5))
- relpath.append(dir.get()).append(includename+=5);
- else
- relpath.append(includename); //still correct for OS
- StringBuffer abspath;
- makeAbsolutePath(relpath.str(), abspath);
- IPropertyTree *res = NULL;
- if (manifest)
- {
- VStringBuffer xpath("Resource[@res_include_path='%s']", abspath.str());
- res = manifest->queryPropTree(xpath.str());
- }
- if (res)
- {
- StringBuffer xslt;
- getResource(res, xslt);
- includebuf.append(xslt.str());
- }
- else if (checkFileExists(abspath.str()))
- {
- Owned <IFile> f = createIFile(abspath.str());
- Owned <IFileIO> fio = f->open(IFOread);
- read(fio, 0, (size32_t) f->size(), includebuf);
- }
- //esp looks in two places for path starting with "xslt/"
- else if (mapEspDirectories && !strncmp(includename, "xslt/", 5))
- {
- StringBuffer relpath(dir.get());
- relpath.append("smc_").append(includename);;
- makeAbsolutePath(relpath.str(), abspath.clear());
- if (checkFileExists(abspath.str()))
- {
- Owned <IFile> f = createIFile(abspath.str());
- Owned <IFileIO> fio = f->open(IFOread);
- read(fio, 0, (size32_t) f->size(), includebuf);
- }
- }
- return true;
- }
- void WuWebView::getResultViewNames(StringArray &names)
- {
- Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements("Views/Results[@name]");
- ForEach(*iter)
- names.append(iter->query().queryProp("@name"));
- }
- void WuWebView::getResource(IPropertyTree *res, StringBuffer &content)
- {
- if (!loadedDll)
- return;
- if (res->hasProp("@id"))
- {
- int id = res->getPropInt("@id");
- size32_t len = 0;
- const void *data = NULL;
- if (loadedDll->getResource(len, data, res->queryProp("@type"), (unsigned) id) && len>0)
- {
- if (res->getPropBool("@compressed"))
- {
- StringBuffer decompressed;
- decompressResource(len, data, content);
- content.append(decompressed.str());
- }
- else
- content.append(len, (const char *)data);
- }
- }
- }
- void WuWebView::getResource(const char *name, StringBuffer &content, StringBuffer &includepath, const char *type)
- {
- VStringBuffer xpath("Resource[@name='%s']", name);
- if (type)
- xpath.append("[@type='").append(type).append("']");
- IPropertyTree *res = ensureManifest()->queryPropTree(xpath.str());
- calculateResourceIncludePaths();
- includepath.append(res->queryProp("@res_include_path"));
- if (res)
- getResource(res, content);
- }
- StringBuffer &WuWebView::aggregateResources(const char *type, StringBuffer &content)
- {
- VStringBuffer xpath("Resource[@type='%s']", type);
- Owned<IPropertyTreeIterator> iter = ensureManifest()->getElements(xpath.str());
- ForEach(*iter)
- getResource(&iter->query(), content);
- return content;
- }
- void WuWebView::getResultXSLT(const char *viewName, StringBuffer &xslt, StringBuffer &abspath)
- {
- VStringBuffer xpath("Views/Results[@name='%s']/@resource", viewName);
- const char *resource = ensureManifest()->queryProp(xpath.str());
- if (resource)
- getResource(resource, xslt, abspath, "XSLT");
- }
- void WuWebView::renderExpandedResults(const char *viewName, WuExpandedResultBuffer &expanded, StringBuffer &out)
- {
- IPropertyTree *mf = ensureManifest();
- VStringBuffer xpath("Views/Results[@name='%s']", viewName);
- IPropertyTree *view = mf->queryPropTree(xpath.str());
- if (!view)
- throw MakeStringException(WUWEBERR_ViewResourceNotFound, "Result view %s not found", viewName);
- expanded.appendXML(view, "view");
- if (loadedDll)
- expanded.appendManifestSchemas(*mf, *loadedDll);
- expanded.finalize();
- const char *type=view->queryProp("@type");
- if (!type)
- throw MakeStringException(WUWEBERR_UnknownViewType, "No type defined for view %s", viewName);
- else if (strieq(type, "xml"))
- {
- out.swapWith(expanded.buffer);
- return;
- }
- else if (!strieq(type, "xslt"))
- throw MakeStringException(WUWEBERR_UnknownViewType, "View %s has an unknown type of %s", viewName, type);
- StringBuffer xslt;
- StringBuffer rootpath;
- getResultXSLT(viewName, xslt, rootpath);
- if (!xslt.length())
- throw MakeStringException(WUWEBERR_ViewResourceNotFound, "XSLT for %s view not found", viewName);
- Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
- t->setIncludeHandler(this);
- StringBuffer cacheId(viewName);
- cacheId.append('@').append(dllname.str()); //using dllname, cloned workunits can share cache entry
- t->setXslSource(xslt.str(), xslt.length(), cacheId.str(), rootpath.str());
- t->setXmlSource(expanded.buffer.str(), expanded.buffer.length());
- t->transform(out);
- }
- void WuWebView::renderResults(const char *viewName, const char *xml, StringBuffer &out)
- {
- WuExpandedResultBuffer buffer(name.str());
- buffer.appendDatasetsFromXML(xml);
- renderExpandedResults(viewName, buffer, out);
- }
- void WuWebView::renderResults(const char *viewName, StringBuffer &out)
- {
- WuExpandedResultBuffer buffer(name.str());
- buffer.appendResults(wu, username.get(), pw.get());
- renderExpandedResults(viewName, buffer, out);
- }
- void WuWebView::renderSingleResult(const char *viewName, const char *resultname, StringBuffer &out)
- {
- WuExpandedResultBuffer buffer(name.str());
- buffer.appendSingleResult(wu, resultname, username.get(), pw.get());
- renderExpandedResults(viewName, buffer, out);
- }
- void WuWebView::applyResultsXSLT(const char *filename, const char *xml, StringBuffer &out)
- {
- WuExpandedResultBuffer buffer(name.str());
- buffer.appendDatasetsFromXML(xml);
- if (loadedDll)
- buffer.appendManifestSchemas(*ensureManifest(), *loadedDll);
- Owned<IXslTransform> t = getXslProcessor()->createXslTransform();
- t->setIncludeHandler(this);
- //override default behavior using filename as cache identifier, there's a chance includes are
- //mapped to resources and need to be distinguished in cache
- StringBuffer cacheId(filename);
- cacheId.append('@').append(dllname.str()); //cloned workunits have same dll and resources
- t->loadXslFromFile(filename, cacheId.str());
- t->setXmlSource(buffer.str(), buffer.length());
- t->transform(out);
- }
- void WuWebView::applyResultsXSLT(const char *filename, StringBuffer &out)
- {
- SCMStringBuffer xml;
- getFullWorkUnitResultsXML(username.get(), pw.get(), wu, xml, false, ExceptionSeverityInformation);
- applyResultsXSLT(filename, xml.str(), out);
- }
- void WuWebView::load(IConstWorkUnit &cwu)
- {
- wu.set(&cwu);
- if (!name.length())
- {
- wu->getJobName(name);
- name.s.replace(' ','_');
- }
- Owned<IConstWUQuery> q = wu->getQuery();
- q->getQueryDllName(dllname);
- try
- {
- loadedDll.setown(queryDllServer().loadDll(dllname.str(), DllLocationAnywhere));
- }
- catch(...)
- {
- DBGLOG("Failed to load %s", dllname.str());
- }
- }
- void WuWebView::load(const char *wuid)
- {
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- Owned<IConstWorkUnit> wu = factory->openWorkUnit(wuid, false);
- if (!wu)
- throw MakeStringException(WUWEBERR_WorkUnitNotFound, "Workunit not found %s", wuid);
- load(*wu);
- }
- extern WUWEBVIEW_API IWuWebView *createWuWebView(IConstWorkUnit &wu, const char *queryname, const char *dir, bool mapEspDirectories)
- {
- try
- {
- return new WuWebView(wu, queryname, dir, mapEspDirectories);
- }
- catch (...)
- {
- SCMStringBuffer wuid;
- DBGLOG("ERROR loading workunit %s shared object.", wu.getWuid(wuid).str());
- }
- return NULL;
- }
- extern WUWEBVIEW_API IWuWebView *createWuWebView(const char *wuid, const char *queryname, const char *dir, bool mapEspDirectories)
- {
- try
- {
- return new WuWebView(wuid, queryname, dir, mapEspDirectories);
- }
- catch (...)
- {
- DBGLOG("ERROR loading workunit %s shared object.", wuid);
- }
- return NULL;
- }
|