00001 #include "xercesc/framework/LocalFileFormatTarget.hpp"
00002 #include "xercesc/framework/MemBufInputSource.hpp"
00003 #include "xercesc/sax/SAXParseException.hpp"
00004 #include "xercesc/sax/EntityResolver.hpp"
00005 #include "xercesc/sax/InputSource.hpp"
00006 #include "xercesc/parsers/XercesDOMParser.hpp"
00007 #include "xercesc/util/PlatformUtils.hpp"
00008 #include "xercesc/util/XercesDefs.hpp"
00009 #include "xercesc/util/XMLUni.hpp"
00010 #include "xercesc/util/XMLURL.hpp"
00011 #include "xercesc/util/XMLString.hpp"
00012 #include "xercesc/dom/DOM.hpp"
00013
00014 #include "GaudiKernel/MsgStream.h"
00015 #include "GaudiKernel/Service.h"
00016 #include "Reflex/PluginService.h"
00017
00018 #include "XMLFileCatalog.h"
00019
00020 #include <fstream>
00021 #include <iostream>
00022 #include <stdexcept>
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include "uuid/uuid.h"
00026
00027 using namespace xercesc;
00028 using namespace Gaudi;
00029 using namespace std;
00030
00031 #if _XERCES_VERSION <= 30000
00032
00033 #define setIdAttribute(a, b) setIdAttribute(a)
00034 #endif
00035
00036 PLUGINSVC_FACTORY(XMLFileCatalog,IInterface*(std::string, IMessageSvc*))
00037
00038 namespace {
00039
00040 typedef const string& CSTR;
00041 inline string _toString(const XMLCh *toTranscode) {
00042 char * buff = XMLString::transcode(toTranscode);
00043 string tmp(buff==0 ? "" : buff);
00044 XMLString::release(&buff);
00045 return tmp;
00046 }
00047 struct __Init {
00048 __Init() {
00049 try { XMLPlatformUtils::Initialize(); }
00050 catch (const XMLException& e) {
00051 cout << "Xerces-c error in initialization:" << _toString(e.getMessage()) << endl;
00052 }
00053 }
00054 ~__Init() {
00055 XMLPlatformUtils::Terminate();
00056 }
00057 };
00058 __Init __In__;
00059
00060 struct XMLStr {
00061 XMLCh* m_xml;
00062 XMLStr(CSTR c) { m_xml=XMLString::transcode(c.c_str()); }
00063 ~XMLStr() { if (m_xml) XMLString::release(&m_xml); }
00064 operator const XMLCh*() const { return m_xml; }
00065 };
00066 struct XMLTag : public XMLStr {
00067 string m_str;
00068 XMLTag(CSTR s) : XMLStr(s), m_str(s) { }
00069 ~XMLTag() { }
00070 operator CSTR () const { return m_str; }
00071 };
00072
00073 bool operator==(CSTR c, const XMLTag& b) { return c==b.m_str; }
00074 struct XMLCollection {
00075 DOMElement* m_node;
00076 XMLCollection(DOMNode* n, bool use_children=true) : m_node((DOMElement*)n) {
00077 if ( use_children ) {
00078 if ( m_node ) m_node = (DOMElement*)m_node->getFirstChild();
00079 if ( m_node && m_node->getNodeType() != DOMNode::ELEMENT_NODE ) ++(*this);
00080 }
00081 }
00082 operator bool() const { return 0 != m_node; }
00083 operator DOMNode* () const { return m_node; }
00084 operator DOMElement* () const { return m_node; }
00085 DOMElement* operator->() const { return m_node; }
00086 string attr(const XMLTag& tag) const { return _toString(m_node->getAttribute(tag));}
00087 string attr(CSTR tag) const { return attr(XMLTag(tag)); }
00088 string tag() const { return _toString(m_node->getTagName()); }
00089 void operator++() {
00090 while(m_node) {
00091 m_node = (DOMElement*)m_node->getNextSibling();
00092 if ( m_node && m_node->getNodeType() == DOMNode::ELEMENT_NODE ) {
00093 return;
00094 }
00095 }
00096 }
00097 };
00098 struct ErrHandler : public ErrorHandler {
00100 IMessageSvc* m_msg;
00102 ErrHandler(IMessageSvc* m) : m_msg(m) {}
00104 void resetErrors() { }
00106 void warning(const SAXParseException& ) { }
00108 void error(const SAXParseException& e);
00110 void fatalError(const SAXParseException& e);
00111 };
00112 struct DTDRedirect : public EntityResolver {
00113 InputSource* resolveEntity(const XMLCh* const , const XMLCh* const ) {
00114 static const char* dtdID = "redirectinmem.dtd";
00115 static const char* dtd = \
00116 "\
00117 <!ELEMENT POOLFILECATALOG (META*,File*)>\
00118 <!ELEMENT META EMPTY>\
00119 <!ELEMENT File (physical,logical,metadata*)>\
00120 <!ATTLIST META name CDATA #REQUIRED>\
00121 <!ATTLIST META type CDATA #REQUIRED>\
00122 <!ELEMENT physical (pfn)+>\
00123 <!ELEMENT logical (lfn)*>\
00124 <!ELEMENT metadata EMPTY>\
00125 <!ELEMENT lfn EMPTY>\
00126 <!ELEMENT pfn EMPTY>\
00127 <!ATTLIST File ID ID #REQUIRED>\
00128 <!ATTLIST pfn name ID #REQUIRED>\
00129 <!ATTLIST pfn filetype CDATA #IMPLIED>\
00130 <!ATTLIST lfn name ID #REQUIRED>\
00131 <!ATTLIST metadata att_name CDATA #REQUIRED>\
00132 <!ATTLIST metadata att_value CDATA #REQUIRED>\
00133 ";
00134 static const size_t len = strlen(dtd);
00135 return new MemBufInputSource((const XMLByte*)dtd,len,dtdID,false);
00136 }
00137 };
00138
00139 void ErrHandler::error(const SAXParseException& e) {
00140 string m(_toString(e.getMessage()));
00141 if (m.find("The values for attribute 'name' must be names or name tokens")!=string::npos ||
00142 m.find("The values for attribute 'ID' must be names or name tokens")!=string::npos ||
00143 m.find("for attribute 'name' must be Name or Nmtoken")!=string::npos ||
00144 m.find("for attribute 'ID' must be Name or Nmtoken")!=string::npos ||
00145 m.find("for attribute 'name' is invalid Name or NMTOKEN value")!=string::npos ||
00146 m.find("for attribute 'ID' is invalid Name or NMTOKEN value")!=string::npos )
00147 return;
00148 string sys(_toString(e.getSystemId()));
00149 MsgStream log(m_msg,"XMLCatalog");
00150 log << MSG::ERROR << "Error at file \"" << sys
00151 << "\", line " << e.getLineNumber() << ", column " << e.getColumnNumber() << endmsg
00152 << "Message: " << m << endmsg;
00153 }
00154 void ErrHandler::fatalError(const SAXParseException& e) {
00155 MsgStream log(m_msg,"XMLCatalog");
00156 string m(_toString(e.getMessage()));
00157 string sys(_toString(e.getSystemId()));
00158 log << MSG::ERROR << "Fatal Error at file \"" << sys
00159 << "\", line " << e.getLineNumber() << ", column " << e.getColumnNumber() << endmsg
00160 << "Message: " << m << endmsg;
00161 throw runtime_error( "Standard pool exception : Fatal Error on the DOM Parser" );
00162 }
00163
00164 const XMLTag EmptyCatalog("<!-- Edited By POOL -->\n"
00165 "<!DOCTYPE POOLFILECATALOG SYSTEM \"InMemory\">\n"
00166 "<POOLFILECATALOG>\n"
00167 "</POOLFILECATALOG>\n");
00168 const XMLTag PFNCOLL ("physical");
00169 const XMLTag LFNCOLL ("logical");
00170 const XMLTag PFNNODE ( "pfn");
00171 const XMLTag LFNNODE ( "lfn");
00172 const XMLTag Attr_type ( "type");
00173 const XMLTag Attr_ID ( "ID");
00174 const XMLTag Attr_name ( "name");
00175 const XMLTag Attr_ftype ( "filetype");
00176 const XMLTag MetaNode ( "metadata");
00177 const XMLTag Attr_metaName ( "att_name");
00178 const XMLTag Attr_metaValue ( "att_value");
00179 }
00180
00182 std::string Gaudi::createGuidAsString() {
00183 char text[64];
00184 uuid_t uuid;
00185 ::uuid_generate_time(uuid);
00186 struct Guid {
00187 unsigned int Data1;
00188 unsigned short Data2;
00189 unsigned short Data3;
00190 unsigned char Data4[8];
00191 } *g = (Guid*)&uuid;
00192 ::sprintf(text, "%08X-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
00193 g->Data1, g->Data2, g->Data3,
00194 g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3],
00195 g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
00196 return text;
00197 }
00198
00199 XMLFileCatalog::XMLFileCatalog(CSTR uri, IMessageSvc* m)
00200 : m_rdOnly(false),m_update(false),m_doc(0),m_parser(0),m_errHdlr(0),
00201 m_file(uri), m_msgSvc(m)
00202 {
00203 }
00204
00205 XMLFileCatalog::~XMLFileCatalog() {
00206 if (m_parser) delete m_parser;
00207 m_parser = 0;
00208 if (m_errHdlr) delete m_errHdlr;
00209 m_errHdlr = 0;
00210 m_doc = 0;
00211 }
00212
00214 std::string XMLFileCatalog::createFID() const {
00215 return createGuidAsString();
00216 }
00217
00218 DOMDocument* XMLFileCatalog::getDoc(bool throw_if_no_exists) const {
00219 if ( !m_doc && throw_if_no_exists )
00220 printError("The XML catalog was not started.",true);
00221 return m_doc;
00222 }
00223
00224 void XMLFileCatalog::printError(CSTR msg, bool rethrow) const {
00225 MsgStream log(m_msgSvc,"XMLCatalog");
00226 log << MSG::FATAL << msg << endmsg;
00227 if ( rethrow ) {
00228 throw runtime_error("XMLFileCatalog> "+msg);
00229 }
00230 }
00231
00232 void XMLFileCatalog::init() {
00233 string xmlFile = getfile(false);
00234 try{
00235 if ( m_parser ) delete m_parser;
00236 m_parser = new XercesDOMParser;
00237 m_parser->setValidationScheme(XercesDOMParser::Val_Auto);
00238 m_parser->setDoNamespaces(false);
00239 DTDRedirect dtdinmem;
00240 m_parser->setEntityResolver(&dtdinmem);
00241 if ( ! m_errHdlr ) m_errHdlr = new ErrHandler(m_msgSvc);
00242 m_parser->setErrorHandler(m_errHdlr);
00243 if ( !xmlFile.empty() ) {
00244 m_parser->parse(xmlFile.c_str());
00245 }
00246 else {
00247 const std::string& s = EmptyCatalog;
00248 MemBufInputSource src((const XMLByte*)s.c_str(),s.length(),"MemCatalog");
00249 m_parser->parse(src);
00250 }
00251 m_doc = m_parser->getDocument();
00252 }
00253 catch (const XMLException& e) {
00254 printError("XML parse error["+xmlFile+"]: "+_toString(e.getMessage()));
00255 }
00256 catch (const DOMException& e) {
00257 printError("XML parse error["+xmlFile+"]: "+_toString(e.getMessage()));
00258 }
00259 catch (...) {
00260 printError("UNKNOWN XML parse error in file "+xmlFile);
00261 }
00262 }
00263
00264 string XMLFileCatalog::lookupFID(const std::string& fid) const {
00265 std::string result;
00266 DOMNode* e = element(fid,false);
00267 e = e ? e->getParentNode() : 0;
00268 e = e ? e->getParentNode() : 0;
00269 if ( e ) {
00270 if ( e->getAttributes() ) {
00271 char* nam = XMLString::transcode(((DOMElement*)e)->getAttribute(Attr_ID));
00272 if ( nam ) result = nam;
00273 XMLString::release(&nam);
00274 }
00275 }
00276 return result;
00277 }
00278
00279 void XMLFileCatalog::getFID(Strings& fids) const {
00280 fids.clear();
00281 DOMNode* fde = getDoc(true)->getElementsByTagName(XMLStr("*"))->item(0);
00282 for(XMLCollection c(child(fde,"File"), false); c; ++c)
00283 fids.push_back(c.attr(Attr_ID));
00284 }
00285
00286 void XMLFileCatalog::getPFN(CSTR fid, Files& files) const {
00287 files.clear();
00288 for(XMLCollection c(child(child(element(fid,false),PFNCOLL),PFNNODE), false); c; ++c)
00289 files.push_back(make_pair(c.attr(Attr_name),c.attr(Attr_ftype)));
00290 }
00291
00292 void XMLFileCatalog::getLFN(CSTR fid, Files& files) const {
00293 files.clear();
00294 for(XMLCollection c(child(child(element(fid,false),LFNCOLL),LFNNODE), false); c; ++c)
00295 files.push_back(make_pair(c.attr(Attr_name),fid));
00296 }
00297
00298 void XMLFileCatalog::getMetaData(CSTR fid, Attributes& attr) const {
00299 attr.clear();
00300 for(XMLCollection c(child(element(fid),MetaNode), false); c; ++c)
00301 attr.push_back(make_pair(c.attr(Attr_metaName),c.attr(Attr_metaValue)));
00302 if ( attr.size() > 0 )
00303 attr.push_back(make_pair("guid",fid));
00304 }
00305
00306 DOMNode* XMLFileCatalog::child(DOMNode* par,CSTR tag,CSTR attr,CSTR val) const {
00307 for(XMLCollection c(par); c; ++c ) {
00308 if( c.tag() == tag ) {
00309 if( !attr.empty() && c.attr(attr) != val) continue;
00310 return c;
00311 }
00312 }
00313 return 0;
00314 }
00315
00316 void XMLFileCatalog::setMetaData(CSTR fid, CSTR attr, CSTR val) const {
00317 if ( !readOnly() ) {
00318 DOMNode* node = element(fid);
00319 DOMElement* mnod = (DOMElement*)child(node,MetaNode,Attr_metaName,attr);
00320 if (!mnod){
00321 mnod = getDoc(true)->createElement(MetaNode);
00322 node->appendChild(mnod);
00323 mnod->setAttribute(Attr_metaName,XMLStr(attr));
00324 }
00325 mnod->setAttribute(Attr_metaValue,XMLStr(val));
00326 m_update = true;
00327 return;
00328 }
00329 printError("Cannot update readonly catalog!");
00330 }
00331
00332 string XMLFileCatalog::getMetaDataItem(CSTR fid,CSTR attr) const {
00333 XMLCollection c(child(getDoc(true)->getElementById(XMLStr(fid)),MetaNode,Attr_metaName,attr));
00334 return c ? c.attr(attr) : string("");
00335 }
00336
00337 void XMLFileCatalog::dropMetaData(CSTR fid,CSTR attr) const {
00338 vector<DOMNode*> gbc;
00339 DOMNode* fn = getDoc(true)->getElementById(XMLStr(fid));
00340 for(XMLCollection c(child(fn,MetaNode)); c; ++c)
00341 if ( attr[0]=='*' || !c.attr(attr).empty() ) gbc.push_back(c);
00342 for(vector<DOMNode*>::iterator i=gbc.begin(); i != gbc.end(); i++)
00343 fn->removeChild(*i);
00344 }
00345
00346 DOMNode* XMLFileCatalog::element(CSTR element_name,bool print_err) const {
00347 DOMNode* node = getDoc(true)->getElementById(XMLStr(element_name));
00348 if ( !node && print_err ) printError("Cannot find element:"+element_name);
00349 return node;
00350 }
00351
00352 void XMLFileCatalog::deleteFID(CSTR fid) const {
00353 DOMNode *pn = 0, *fn = element(fid);
00354 if ( fn ) pn = fn->getParentNode();
00355 if ( pn ) pn->removeChild(fn);
00356 }
00357
00358 void XMLFileCatalog::registerFID(CSTR fid) const {
00359 if ( !fid.empty() ) {
00360 std::pair<DOMElement*, DOMElement*> res = i_registerFID(fid);
00361 if ( res.first == 0 || res.second == 0 ) {
00362 printError("Failed to register FID:"+fid);
00363 }
00364 return;
00365 }
00366 throw runtime_error("XMLFileCatalog> Cannot register LFN for invalid FID:"+fid);
00367 }
00368
00369 std::pair<DOMElement*,DOMElement*> XMLFileCatalog::i_registerFID(CSTR fid) const {
00370 if ( !readOnly() ) {
00372 DOMElement *file = (DOMElement*)element(fid,false), *phyelem = 0, *logelem = 0;
00373 DOMDocument* doc = getDoc(true);
00374 if ( !file ) {
00375 DOMNode* fde = doc->getElementsByTagName(XMLStr("*"))->item(0);
00376 file = m_doc->createElement(XMLStr("File"));
00377 file->setAttribute(Attr_ID, XMLStr(fid));
00378 file->setIdAttribute(Attr_ID, true);
00379 fde->appendChild(file);
00380 m_update = true;
00381 }
00382 for(XMLCollection c1(file); c1; ++c1 ) {
00383 char* nam = XMLString::transcode(c1->getNodeName());
00384 if ( nam == PFNCOLL ) phyelem = c1;
00385 if ( nam == LFNCOLL ) logelem = c1;
00386 XMLString::release(&nam);
00387 }
00388 if ( !phyelem ) {
00389 phyelem = doc->createElement(PFNCOLL);
00390 file->appendChild(phyelem);
00391 m_update = true;
00392 }
00393 if ( !logelem ) {
00394 logelem = doc->createElement(LFNCOLL);
00395 file->appendChild(logelem);
00396 m_update = true;
00397 }
00398 return std::make_pair(logelem,phyelem);
00399 }
00400 printError("Cannot update readonly catalog!");
00401 return std::pair<DOMElement*, DOMElement*>(0,0);
00402 }
00403
00404 void XMLFileCatalog::registerPFN(CSTR fid, CSTR pfn, CSTR ftype) const {
00405 if ( !fid.empty() ) {
00406 std::pair<DOMElement*,DOMElement*> res = i_registerFID(fid);
00407 DOMElement* phyelem = res.second, *fnelem = 0;
00408 for(XMLCollection c(phyelem); c; ++c ) {
00409 char* nam = XMLString::transcode(c->getNodeName());
00410 if ( nam == PFNNODE ) {
00411 XMLString::release(&nam);
00412 nam = XMLString::transcode(c->getAttribute(Attr_name));
00413 if ( nam == pfn ) {
00414 XMLString::release(&nam);
00415 fnelem = c;
00416 break;
00417 }
00418 }
00419 XMLString::release(&nam);
00420 }
00421 if ( !fnelem ) {
00422 fnelem = getDoc(true)->createElement(PFNNODE);
00423 phyelem->appendChild(fnelem);
00424 fnelem->setAttribute(Attr_ftype,XMLStr(ftype));
00425 fnelem->setAttribute(Attr_name,XMLStr(pfn));
00426 fnelem->setIdAttribute(Attr_name, true);
00427 m_update = true;
00428 }
00429 return;
00430 }
00431 throw runtime_error("XMLFileCatalog> Cannot register PFN for invalid FID:"+fid);
00432 }
00433
00434 void XMLFileCatalog::registerLFN(CSTR fid, CSTR lfn) const {
00435 if ( !fid.empty() ) {
00436 std::pair<DOMElement*, DOMElement*> res = i_registerFID(fid);
00437 DOMElement* logelem = res.first, *fnelem = 0;
00438 for(XMLCollection c(logelem); c; ++c ) {
00439 char* nam = XMLString::transcode(c->getNodeName());
00440 if ( nam == LFNNODE ) {
00441 XMLString::release(&nam);
00442 nam = XMLString::transcode(c->getAttribute(Attr_name));
00443 if ( nam == lfn ) {
00444 XMLString::release(&nam);
00445 fnelem = c;
00446 break;
00447 }
00448 }
00449 }
00450 if ( !fnelem ) {
00451 fnelem = getDoc(true)->createElement(LFNNODE);
00452 logelem->appendChild(fnelem);
00453 fnelem->setAttribute(Attr_name,XMLStr(lfn));
00454 fnelem->setIdAttribute(Attr_name, true);
00455 m_update = true;
00456 }
00457 return;
00458 }
00459 throw runtime_error("XMLFileCatalog> Cannot register LFN for invalid FID:"+fid);
00460 }
00461
00462 void XMLFileCatalog::commit() {
00463 try {
00464 if ( dirty() && !readOnly() ) {
00465 string xmlfile = getfile(true);
00466 XMLStr ii("LS");
00467 DOMImplementation *imp = DOMImplementationRegistry::getDOMImplementation(ii);
00468 XMLFormatTarget *tar = new LocalFileFormatTarget(xmlfile.c_str());
00469 #if _XERCES_VERSION <= 30000
00470 DOMWriter *wr = imp->createDOMWriter();
00471 wr->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);
00472 wr->writeNode(tar, *m_doc);
00473 wr->release();
00474 #else
00475 DOMLSOutput *output = imp->createLSOutput();
00476 output->setByteStream(tar);
00477 DOMLSSerializer *wr = imp->createLSSerializer();
00478 wr->getDomConfig()->setParameter(XMLStr("format-pretty-print"), true);
00479 wr->write(m_doc, output);
00480 output->release();
00481 wr->release();
00482 #endif
00483 delete tar;
00484 }
00485 }
00486 catch ( exception& e ) {
00487 printError(string("Cannot open output file:")+e.what());
00488 }
00489 catch (...) {
00490 printError("Unknown IO rrror.");
00491 }
00492 }
00493
00494 string XMLFileCatalog::getfile(bool create) {
00495 string protocol, path;
00496 XMLURL xerurl;
00497 try{
00498 xerurl = (const XMLCh*)XMLStr(m_file);
00499 protocol = _toString(xerurl.getProtocolName());
00500 path = _toString(xerurl.getPath());
00501 }
00502 catch (const XMLException& e ) {
00503 printError(_toString(e.getMessage()));
00504 }
00505 if ( protocol.empty() ) {
00506 printError("Missing protocol.");
00507 }
00508 else if ( protocol == "http" || protocol == "ftp" ) {
00509 m_rdOnly = true;
00510 }
00511 else if ( protocol == "file" ) {
00512 m_rdOnly = false;
00513 struct stat buff;
00514 int exist = ::stat(path.c_str(),&buff) != -1;
00515 if ( create && !exist ) {
00516 MsgStream log(m_msgSvc,"XMLCatalog");
00517 log << MSG::INFO << "File '" << path << "' does not exist. New file created." << endmsg;
00518 ofstream out(path.c_str());
00519 if( !m_rdOnly && out.is_open() ) {
00520 out << (CSTR)EmptyCatalog << endl;
00521 }
00522 else {
00523 printError("Problem creating file "+path);
00524 }
00525 out.close();
00526 }
00527 else if ( exist ) {
00528 return path;
00529 }
00530 else if ( !create ) {
00531 return "";
00532 }
00533 }
00534 else {
00535 printError(protocol + ": protocol not supported.");
00536 }
00537 return path;
00538 }