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