00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "RootCnv/RootDataConnection.h"
00012 #include "RootUtils.h"
00013 #include "GaudiKernel/IOpaqueAddress.h"
00014 #include "GaudiKernel/LinkManager.h"
00015 #include "GaudiKernel/strcasecmp.h"
00016 #include "GaudiKernel/DataObject.h"
00017 #include "GaudiKernel/IRegistry.h"
00018 #include "GaudiKernel/MsgStream.h"
00019
00020 #include "TROOT.h"
00021 #include "TFile.h"
00022 #include "TTree.h"
00023 #include "TLeaf.h"
00024 #include "TClass.h"
00025 #include "TBranch.h"
00026 #include "Compression.h"
00027 #include "TTreePerfStats.h"
00028
00029
00030 #include <stdexcept>
00031
00032 using namespace Gaudi;
00033 using namespace std;
00034 typedef const string& CSTR;
00035
00036 static string s_empty;
00037 static string s_local = "<localDB>";
00038
00039 #ifdef __POOL_COMPATIBILITY
00040 #include "PoolTool.h"
00041 #endif
00042 #include "RootTool.h"
00043
00044 static int s_compressionLevel = ROOT::CompressionSettings(ROOT::kLZMA,6);
00045
00046 static bool match_wild(const char *str, const char *pat) {
00047
00048
00049
00050 static char table[256];
00051 static bool first = true;
00052 const char *s, *p;
00053 bool star = false;
00054 if ( first ) {
00055 for (int i = 0; i < 256; ++i) table[i] = char(i);
00056 first = false;
00057 }
00058 loopStart:
00059 for (s = str, p = pat; *s; ++s, ++p) {
00060 switch (*p) {
00061 case '?':
00062 if (*s == '.') goto starCheck;
00063 break;
00064 case '*':
00065 star = true;
00066 str = s, pat = p;
00067 do { ++pat; } while (*pat == '*');
00068 if (!*pat) return true;
00069 goto loopStart;
00070 default:
00071 if ( *(table+*s) != *(table+*p) )
00072 goto starCheck;
00073 break;
00074 }
00075 }
00076 while (*p == '*') ++p;
00077 return (!*p);
00078
00079 starCheck:
00080 if (!star) return false;
00081 str++;
00082 goto loopStart;
00083 }
00084
00086 RootConnectionSetup::RootConnectionSetup() : refCount(1), m_msgSvc(0)
00087 {
00088 }
00089
00091 RootConnectionSetup::~RootConnectionSetup() {
00092 deletePtr(m_msgSvc);
00093 }
00094
00096 long RootConnectionSetup::setCompression(const std::string& compression) {
00097 int res = 0, level = ROOT::CompressionSettings(ROOT::kLZMA,6);
00098 size_t idx = compression.find(':');
00099 if ( idx != string::npos ) {
00100 string alg = compression.substr(0,idx);
00101 ROOT::ECompressionAlgorithm alg_code = ROOT::kUseGlobalSetting;
00102 if ( strcasecmp(alg.c_str(),"ZLIB") == 0 )
00103 alg_code = ROOT::kZLIB;
00104 else if ( strcasecmp(alg.c_str(),"LZMA") == 0 )
00105 alg_code = ROOT::kLZMA;
00106 else
00107 throw runtime_error("ERROR: request to set unknown ROOT compression algorithm:"+alg);
00108 res = ::sscanf(compression.c_str()+idx+1,"%d",&level);
00109 if ( res == 1 ) {
00110 s_compressionLevel = ROOT::CompressionSettings(alg_code,level);
00111 return StatusCode::SUCCESS;
00112 }
00113 throw runtime_error("ERROR: request to set unknown ROOT compression level:"+compression.substr(idx+1));
00114 }
00115 else if ( 1==::sscanf(compression.c_str(),"%d",&level) ) {
00116 s_compressionLevel = level;
00117 return StatusCode::SUCCESS;
00118 }
00119 throw runtime_error("ERROR: request to set unknown ROOT compression mechanism:"+compression);
00120 }
00121
00123 int RootConnectionSetup::compression() {
00124 return s_compressionLevel;
00125 }
00126
00128 void RootConnectionSetup::addRef() {
00129 ++refCount;
00130 }
00131
00132
00133 void RootConnectionSetup::release() {
00134 int tmp = --refCount;
00135 if ( tmp <= 0 ) {
00136 delete this;
00137 }
00138 }
00139
00140
00141 void RootConnectionSetup::setMessageSvc(MsgStream* m) {
00142 MsgStream* tmp = m_msgSvc;
00143 m_msgSvc = m;
00144 deletePtr(tmp);
00145 }
00146
00147
00148 RootDataConnection::RootDataConnection(const IInterface* owner, CSTR fname, RootConnectionSetup* setup)
00149 : IDataConnection(owner,fname), m_setup(setup), m_statistics(0), m_tool(0)
00150 {
00151
00152 if ( fname.length() == 36 && fname[8]=='-'&&fname[13]=='-'&&fname[18]=='-'&&fname[23]=='-' ) {
00153 m_name = "FID:"+fname;
00154 }
00155 m_setup->addRef();
00156 m_age = 0;
00157 m_file = 0;
00158 m_refs = 0;
00159 addClient(owner);
00160 }
00161
00162
00163 RootDataConnection::~RootDataConnection() {
00164 m_setup->release();
00165 releasePtr(m_tool);
00166 }
00167
00168
00169 void RootDataConnection::addClient(const IInterface* client) {
00170 m_clients.insert(client);
00171 }
00172
00173
00174 size_t RootDataConnection::removeClient(const IInterface* client) {
00175 Clients::iterator i=m_clients.find(client);
00176 if ( i != m_clients.end() ) m_clients.erase(i);
00177 return m_clients.size();
00178 }
00179
00180
00181 bool RootDataConnection::lookupClient(const IInterface* client) const {
00182 Clients::const_iterator i=m_clients.find(client);
00183 return i != m_clients.end();
00184 }
00185
00186
00187 void RootDataConnection::saveStatistics(CSTR statisticsFile) {
00188 if ( m_statistics ) {
00189 m_statistics->Print();
00190 if ( !statisticsFile.empty() )
00191 m_statistics->SaveAs(statisticsFile.c_str());
00192 deletePtr(m_statistics);
00193 }
00194 }
00195
00196
00197 void RootDataConnection::enableStatistics(CSTR section) {
00198 if ( 0 == m_statistics ) {
00199 TTree* t=getSection(section,false);
00200 if ( t ) {
00201 m_statistics = new TTreePerfStats((section+"_ioperf").c_str(),t);
00202 return;
00203 }
00204 msgSvc() << MSG::WARNING << "Failed to enable perfstats for tree:" << section << endmsg;
00205 return;
00206 }
00207 msgSvc() << MSG::INFO << "Perfstats are ALREADY ENABLED." << endmsg;
00208 }
00209
00210
00211 RootDataConnection::Tool* RootDataConnection::makeTool() {
00212 releasePtr(m_tool);
00213 if ( !m_refs ) m_refs = (TTree*)m_file->Get("Refs");
00214 if ( m_refs )
00215 m_tool = new RootTool(this);
00216 #ifdef __POOL_COMPATIBILITY
00217 else if ( m_file->Get("##Links") != 0 )
00218 m_tool = new PoolTool(this);
00219 #endif
00220 return m_tool;
00221 }
00222
00223
00224 StatusCode RootDataConnection::connectRead() {
00225 m_file = TFile::Open(m_pfn.c_str());
00226 if ( m_file && !m_file->IsZombie() ) {
00227 StatusCode sc = StatusCode::FAILURE;
00228 msgSvc() << MSG::DEBUG << "Opened file " << m_pfn << " in mode READ. [" << m_fid << "]" << endmsg << MSG::DEBUG;
00229 if ( msgSvc().isActive() ) m_file->ls();
00230 msgSvc() << MSG::VERBOSE;
00231 if ( msgSvc().isActive() ) m_file->Print();
00232 if ( makeTool() ) sc = m_tool->readRefs();
00233 if ( sc.isSuccess() ) {
00234 bool need_fid = m_fid == m_pfn;
00235 string fid = m_fid;
00236 m_mergeFIDs.clear();
00237 for(size_t i=0, n=m_params.size(); i<n; ++i) {
00238 if ( m_params[i].first == "FID" ) {
00239 m_mergeFIDs.push_back(m_params[i].second);
00240 if ( m_params[i].second != m_fid ) {
00241 msgSvc() << MSG::DEBUG << "Check FID param:" << m_params[i].second << endmsg;
00242
00243 m_fid = m_params[i].second;
00244
00245 }
00246 }
00247 }
00248 if ( !need_fid && fid != m_fid ) {
00249 msgSvc() << MSG::ERROR << "FID mismatch:" << fid << "(Catalog) != " << m_fid << "(file)" << endmsg
00250 << "for PFN:" << m_pfn << endmsg;
00251 return StatusCode::FAILURE;
00252 }
00253 msgSvc() << MSG::DEBUG << "Using FID " << m_fid << " from params table...." << endmsg
00254 << "for PFN:" << m_pfn << endmsg;
00255 return sc;
00256 }
00257 }
00258 else if ( m_file ) {
00259 deletePtr(m_file);
00260 }
00261 return StatusCode::FAILURE;
00262 }
00263
00264
00265 StatusCode RootDataConnection::connectWrite(IoType typ) {
00266 int compress = RootConnectionSetup::compression();
00267 msgSvc() << MSG::DEBUG;
00268 switch(typ) {
00269 case CREATE:
00270 resetAge();
00271 m_file = TFile::Open(m_pfn.c_str(),"CREATE","Root event data",compress);
00272 m_refs = new TTree("Refs","Root reference data");
00273 msgSvc() << "Opened file " << m_pfn << " in mode CREATE. [" << m_fid << "]" << endmsg;
00274 m_params.push_back(make_pair("PFN",m_pfn));
00275 if ( m_fid != m_pfn ) {
00276 m_params.push_back(make_pair("FID",m_fid));
00277 }
00278 makeTool();
00279 break;
00280 case RECREATE:
00281 resetAge();
00282 m_file = TFile::Open(m_pfn.c_str(),"RECREATE","Root event data",compress);
00283 msgSvc() << "Opened file " << m_pfn << " in mode RECREATE. [" << m_fid << "]" << endmsg;
00284 m_refs = new TTree("Refs","Root reference data");
00285 m_params.push_back(make_pair("PFN",m_pfn));
00286 if ( m_fid != m_pfn ) {
00287 m_params.push_back(make_pair("FID",m_fid));
00288 }
00289 makeTool();
00290 break;
00291 case UPDATE:
00292 resetAge();
00293 m_file = TFile::Open(m_pfn.c_str(),"UPDATE","Root event data",compress);
00294 msgSvc() << "Opened file " << m_pfn << " in mode UPDATE. [" << m_fid << "]" << endmsg;
00295 if ( m_file && !m_file->IsZombie() ) {
00296 if ( makeTool() ) return m_tool->readRefs();
00297 TDirectory::TContext ctxt(m_file);
00298 m_refs = new TTree("Refs","Root reference data");
00299 makeTool();
00300 return StatusCode::SUCCESS;
00301 }
00302 break;
00303 default:
00304 m_refs = 0;
00305 m_file = 0;
00306 return StatusCode::FAILURE;
00307 }
00308 return 0==m_file ? StatusCode::FAILURE : StatusCode::SUCCESS;
00309 }
00310
00311
00312 StatusCode RootDataConnection::disconnect() {
00313 if ( m_file ) {
00314 if ( !m_file->IsZombie() ) {
00315 if ( m_file->IsWritable() ) {
00316 msgSvc() << MSG::DEBUG;
00317 TDirectory::TContext ctxt(m_file);
00318 if ( m_refs ) {
00319 m_tool->saveRefs().ignore();
00320 m_refs->Write();
00321 }
00322 for(Sections::iterator i=m_sections.begin(); i!= m_sections.end();++i) {
00323 if ( (*i).second ) {
00324 (*i).second->Write();
00325 msgSvc() << "Disconnect section " << (*i).first << " " << (*i).second->GetName() << endmsg;
00326 }
00327 }
00328 m_sections.clear();
00329 }
00330 msgSvc() << MSG::DEBUG;
00331 if ( msgSvc().isActive() ) m_file->ls();
00332 msgSvc() << MSG::VERBOSE;
00333 if ( msgSvc().isActive() ) m_file->Print();
00334 m_file->Close();
00335 }
00336 msgSvc() << MSG::DEBUG << "Disconnected file " << m_pfn << " " << m_file->GetName() << endmsg;
00337 deletePtr(m_file);
00338 releasePtr(m_tool);
00339 }
00340 return StatusCode::SUCCESS;
00341 }
00342
00343
00344 TTree* RootDataConnection::getSection(CSTR section, bool create) {
00345 TTree* t = m_sections[section];
00346 if ( !t ) {
00347 t = (TTree*)m_file->Get(section.c_str());
00348 if ( !t && create ) {
00349 TDirectory::TContext ctxt(m_file);
00350 t = new TTree(section.c_str(),"Root data for Gaudi");
00351 }
00352 if ( t ) {
00353 int cacheSize = m_setup->cacheSize;
00354 if ( create ) {
00355
00356 }
00357 if ( section == m_setup->loadSection && cacheSize>-2 ) {
00358 MsgStream& msg = msgSvc();
00359 int learnEntries = m_setup->learnEntries;
00360 t->SetCacheSize(cacheSize);
00361 t->SetCacheLearnEntries(learnEntries);
00362 msg << MSG::DEBUG;
00363 if ( create ) {
00364 msg << "Tree:" << section << "Setting up tree cache:" << cacheSize << endmsg;
00365 }
00366 else {
00367 const StringVec& vB = m_setup->vetoBranches;
00368 const StringVec& cB = m_setup->cacheBranches;
00369 msg << "Tree:" << section << " Setting up tree cache:" << cacheSize << " Add all branches." << endmsg;
00370 msg << "Tree:" << section << " Learn for " << learnEntries << " entries." << endmsg;
00371
00372 if ( cB.size()==0 && vB.size()== 0 ) {
00373 msg << "Adding (default) all branches to tree cache." << endmsg;
00374 t->AddBranchToCache("*",kTRUE);
00375 }
00376 if ( cB.size()==1 && cB[0]=="*" ) {
00377 msg << "Adding all branches to tree cache according to option \"CacheBranches\"." << endmsg;
00378 t->AddBranchToCache("*",kTRUE);
00379 }
00380 else {
00381 StringVec::const_iterator i;
00382 for(TIter it(t->GetListOfBranches()); it.Next(); ) {
00383 const char* n = ((TNamed*)(*it))->GetName();
00384 bool add = false, veto = false;
00385 for(i=cB.begin(); i != cB.end();++i) {
00386 if ( !match_wild(n,(*i).c_str()) ) continue;
00387 add = true;
00388 break;
00389 }
00390 for(i=vB.begin(); !add && i!=vB.end();++i) {
00391 if ( !match_wild(n,(*i).c_str()) ) continue;
00392 veto = true;
00393 break;
00394 }
00395 if ( add && !veto ) {
00396 msg << "Add " << n << " to branch cache." << endmsg;
00397 t->AddBranchToCache(n,kTRUE);
00398 }
00399 else {
00400 msg << "Do not cache branch " << n << endmsg;
00401 }
00402 }
00403 }
00404 }
00405 }
00406 m_sections[section] = t;
00407 }
00408 }
00409 return t;
00410 }
00411
00412
00413 TBranch* RootDataConnection::getBranch(CSTR section, CSTR branch_name, TClass* cl, void* ptr) {
00414 string n = branch_name+".";
00415 for(int i=0, m=n.length()-1; i<m; ++i) if ( !isalnum(n[i]) ) n[i]='_';
00416 TTree* t = getSection(section,true);
00417 TBranch* b = t->GetBranch(n.c_str());
00418 if ( !b && cl && m_file->IsWritable() ) {
00419 b = t->Branch(n.c_str(),cl->GetName(),(void*)(ptr ? &ptr : 0));
00420 }
00421 if ( !b ) {
00422 b = t->GetBranch(branch_name.c_str());
00423 }
00424 if ( b ) {
00425 b->SetAutoDelete(kFALSE);
00426 }
00427 return b;
00428 }
00429
00430
00431 int RootDataConnection::makeLink(CSTR p) {
00432 int cnt = 0;
00433 StringVec::iterator ip;
00434 for(ip=m_links.begin();ip!=m_links.end();++ip,++cnt) {
00435 if( (*ip) == p ) return cnt;
00436 }
00437 m_links.push_back(p);
00438 return m_links.size()-1;
00439 }
00440
00441
00442 CSTR RootDataConnection::getDb(int which) const {
00443 if ( (which>=0) && (size_t(which)<m_dbs.size()) ) {
00444 if ( *(m_dbs.begin()+which) == s_local ) return m_fid;
00445 return *(m_dbs.begin()+which);
00446 }
00447 return s_empty;
00448 }
00449
00450
00451 CSTR RootDataConnection::empty() const {
00452 return s_empty;
00453 }
00454
00455
00456 pair<int,unsigned long>
00457 RootDataConnection::saveObj(CSTR section, CSTR cnt, TClass* cl, DataObject* pObj,bool fill) {
00458 DataObjectPush push(pObj);
00459 return save(section,cnt,cl,pObj,fill);
00460 }
00461
00462
00463 pair<int,unsigned long>
00464 RootDataConnection::save(CSTR section, CSTR cnt, TClass* cl, void* pObj, bool fill_missing) {
00465 TBranch* b = getBranch(section, cnt, cl, (void*)(pObj ? &pObj : 0));
00466 if ( b ) {
00467 Long64_t evt = b->GetEntries();
00468 if ( fill_missing ) {
00469 Long64_t num, nevt = b->GetTree()->GetEntries();
00470 if ( nevt > evt ) {
00471 b->SetAddress(0);
00472 num = nevt - evt;
00473 while( num > 0 ) { b->Fill(); --num; }
00474 msgSvc() << MSG::DEBUG << "Added " << long(nevt-evt)
00475 << " / Tree: " << nevt << " / Branch: " << b->GetEntries()+1
00476 << " NULL entries to:" << cnt << endmsg;
00477 evt = b->GetEntries();
00478 }
00479 }
00480 b->SetAddress(&pObj);
00481 return make_pair(b->Fill(),(unsigned long)evt);
00482 }
00483 else if ( 0 != pObj ) {
00484 msgSvc() << MSG::ERROR << "Failed to access branch " << m_name << "/" << cnt << endmsg;
00485 }
00486 return make_pair(-1,~0);
00487 }
00488
00489
00490 int RootDataConnection::loadObj(CSTR section, CSTR cnt, unsigned long entry, DataObject*& pObj) {
00491 TBranch* b = getBranch(section,cnt);
00492 if ( b ) {
00493 TClass* cl = gROOT->GetClass(b->GetClassName(),kTRUE);
00494 if ( cl ) {
00495 int nb = -1;
00496 pObj = (DataObject*)cl->New();
00497 {
00498 DataObjectPush push(pObj);
00499 b->SetAddress(&pObj);
00500 if ( section == m_setup->loadSection ) {
00501 TTree* t = b->GetTree();
00502 if ( Long64_t(entry) != t->GetReadEntry() ) {
00503 t->LoadTree(Long64_t(entry));
00504 }
00505 }
00506 nb = b->GetEntry(entry);
00507 msgSvc() << MSG::VERBOSE;
00508 if ( msgSvc().isActive() ) {
00509 msgSvc() << "Load [" << entry << "] --> " << section
00510 << ":" << cnt << " " << nb << " bytes."
00511 << endmsg;
00512 }
00513 if ( nb == 0 && pObj->clID() == CLID_DataObject) {
00514 TFile* f = b->GetFile();
00515 int vsn = f->GetVersion();
00516 if ( vsn < 52400 ) {
00517
00518
00519 nb = 1;
00520 }
00521 else if ( vsn>1000000 && (vsn%1000000)<52400 ) {
00522
00523
00524 nb = 1;
00525 }
00526 }
00527 if ( nb < 0 ) {
00528 delete pObj;
00529 pObj = 0;
00530 }
00531 }
00532 return nb;
00533 }
00534 }
00535 return -1;
00536 }
00537
00538
00539 pair<const RootRef*,const RootDataConnection::ContainerSection*>
00540 RootDataConnection::getMergeSection(const string& container, int entry) const {
00541
00542
00543 MergeSections::const_iterator i=m_mergeSects.find(container);
00544 if ( i != m_mergeSects.end() ) {
00545 size_t cnt = 0;
00546 const ContainerSections& s = (*i).second;
00547 for(ContainerSections::const_iterator j=s.begin(); j != s.end(); ++j,++cnt) {
00548 const ContainerSection& c = *j;
00549 if ( entry >= c.start && entry < (c.start+c.length) ) {
00550 if ( m_linkSects.size() > cnt ) {
00551 if ( msgSvc().isActive() ) {
00552 msgSvc() << MSG::VERBOSE << "MergeSection for:" << container
00553 << " [" << entry << "]" << endmsg
00554 << "FID:" << m_fid << " -> PFN:" << m_pfn << endmsg;
00555 }
00556 return make_pair(&(m_linkSects[cnt]), &c);
00557 }
00558 }
00559 }
00560 }
00561 msgSvc() << MSG::DEBUG << "Return INVALID MergeSection for:" << container
00562 << " [" << entry << "]" << endmsg
00563 << "FID:" << m_fid << " -> PFN:" << m_pfn << endmsg;
00564 return make_pair((const RootRef*)0,(const ContainerSection*)0);
00565 }
00566
00567
00568 void RootDataConnection::makeRef(IRegistry* pR, RootRef& ref) {
00569 IOpaqueAddress* pA = pR->address();
00570 makeRef(pR->name(),pA->clID(),pA->svcType(),pA->par()[0],pA->par()[1],-1,ref);
00571 }
00572
00573
00574 void RootDataConnection::makeRef(CSTR name, long clid, int tech, CSTR dbase, CSTR cnt, int entry, RootRef& ref) {
00575 string db(dbase);
00576 int cdb=-1, ccnt=-1, clnk=-1;
00577 StringVec::iterator idb, icnt, ilnk;
00578 if ( db == m_fid ) {
00579 db = s_local;
00580 }
00581 ref.entry = entry;
00582 if ( !db.empty() ) {
00583 for(cdb=0,idb=m_dbs.begin(); idb!=m_dbs.end();++idb,++cdb)
00584 if( (*idb) == db ) break;
00585 if ( idb == m_dbs.end() ) {
00586 cdb = m_dbs.size();
00587 m_dbs.push_back(db);
00588 }
00589 }
00590 if ( !cnt.empty() ) {
00591 for(ccnt=0,icnt=m_conts.begin(); icnt!=m_conts.end();++icnt,++ccnt)
00592 if( (*icnt) == cnt ) break;
00593 if ( icnt == m_conts.end() ) {
00594 ccnt = m_conts.size();
00595 m_conts.push_back(cnt);
00596 }
00597 }
00598 if ( !name.empty() ) {
00599 for(clnk=0,ilnk=m_links.begin(); ilnk!=m_links.end();++ilnk,++clnk)
00600 if( (*ilnk) == name ) break;
00601 if ( ilnk == m_links.end() ) {
00602 clnk = m_links.size();
00603 m_links.push_back(name);
00604 }
00605 }
00606 ref.dbase = cdb;
00607 ref.container = ccnt;
00608 ref.link = clnk;
00609 ref.clid = clid;
00610 ref.svc = tech;
00611 if ( ref.svc == POOL_ROOT_StorageType ||
00612 ref.svc == POOL_ROOTKEY_StorageType ||
00613 ref.svc == POOL_ROOTTREE_StorageType ) {
00614 ref.svc = ROOT_StorageType;
00615 }
00616 }
00617