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