00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "GaudiKernel/MsgStream.h"
00014 #include "GaudiKernel/IRegistry.h"
00015 #include "GaudiKernel/IUpdateable.h"
00016 #include "GaudiKernel/IIncidentSvc.h"
00017 #include "GaudiKernel/IDataManagerSvc.h"
00018 #include "GaudiKernel/IDataProviderSvc.h"
00019 #include "GaudiKernel/KeyedContainer.h"
00020 #include "GaudiKernel/DataIncident.h"
00021 #include "GaudiKernel/LinkManager.h"
00022 #include "GaudiKernel/Incident.h"
00023 #include "GaudiKernel/System.h"
00024 #include "GaudiUtils/IIODataManager.h"
00025 #include "RootCnv/RootRefs.h"
00026 #include "RootCnv/RootCnvSvc.h"
00027 #include "RootCnv/RootAddress.h"
00028 #include "RootCnv/RootConverter.h"
00029 #include "RootCnv/RootDatabaseCnv.h"
00030 #include "RootCnv/RootDirectoryCnv.h"
00031 #include "RootCnv/RootNTupleCnv.h"
00032 #include "RootCnv/RootDataConnection.h"
00033 #include "RootUtils.h"
00034
00035
00036 #include "TROOT.h"
00037 #include "TClass.h"
00038 #include "TTree.h"
00039 #include "TBranch.h"
00040
00041 using namespace std;
00042 using namespace Gaudi;
00043 typedef const string& CSTR;
00044
00045 #define S_OK StatusCode::SUCCESS
00046 #define S_FAIL StatusCode::FAILURE
00047 namespace GaudiRoot { bool patchStreamers(MsgStream& log); }
00048
00049 namespace {
00050 static map<string, TClass*> s_classesNames;
00051 static map<CLID, TClass*> s_classesClids;
00052 }
00053 #define MBYTE 1024*1024
00054
00055
00056 RootCnvSvc::RootCnvSvc(CSTR nam, ISvcLocator* svc)
00057 : ConversionSvc( nam, svc, ROOT_StorageType),
00058 m_ioMgr(0), m_incidentSvc(0), m_current(0), m_setup(0)
00059 {
00060 m_classRefs = m_classDO = 0;
00061 m_setup = new RootConnectionSetup();
00062 m_setup->cacheBranches.push_back("*");
00063 declareProperty("IOPerfStats", m_ioPerfStats);
00064 declareProperty("ShareFiles", m_shareFiles = "NO");
00065 declareProperty("EnableIncident", m_incidentEnabled = true);
00066 declareProperty("RecordsName", m_recordName = "/FileRecords");
00067
00068 declareProperty("BasketSize", m_setup->basketSize = 40*MBYTE);
00069 declareProperty("CacheSize", m_setup->cacheSize = 10*MBYTE);
00070 declareProperty("AutoFlush", m_setup->autoFlush = 100);
00071 declareProperty("LearnEntries", m_setup->learnEntries = 10);
00072 declareProperty("LoadSection", m_setup->loadSection = "Event");
00073 declareProperty("CacheBranches", m_setup->cacheBranches);
00074 declareProperty("VetoBranches", m_setup->vetoBranches);
00075 }
00076
00077
00078 RootCnvSvc::~RootCnvSvc() {
00079 if (m_setup) m_setup->release();
00080 }
00081
00082
00083 StatusCode RootCnvSvc::error(CSTR msg) {
00084 if ( m_log ) {
00085 log() << MSG::ERROR << "Error: " << msg << endmsg;
00086 return S_FAIL;
00087 }
00088 MsgStream m(msgSvc(),name());
00089 m << MSG::ERROR << "Error: " << msg << endmsg;
00090 return S_FAIL;
00091 }
00092
00093
00094 StatusCode RootCnvSvc::initialize() {
00095 string cname;
00096 StatusCode status = ConversionSvc::initialize();
00097 if ( !status.isSuccess() )
00098 return error("Failed to initialize ConversionSvc base class.");
00099 m_log = new MsgStream(msgSvc(),name());
00100 if( !(status=service("IODataManager", m_ioMgr)).isSuccess() )
00101 return error("Unable to localize interface from service:IODataManager");
00102 if( !(status=service("IncidentSvc", m_incidentSvc)).isSuccess() )
00103 return error("Unable to localize interface from service:IncidentSvc");
00104 m_setup->setMessageSvc(new MsgStream(msgSvc(),name()));
00105 GaudiRoot::patchStreamers(log());
00106 cname = System::typeinfoName(typeid(DataObject));
00107 m_classDO = gROOT->GetClass(cname.c_str());
00108 if ( 0 == m_classDO )
00109 return error("Unable to load class description for DataObject");
00110 cname = System::typeinfoName(typeid(RootObjectRefs));
00111 m_classRefs = gROOT->GetClass(cname.c_str());
00112 if ( 0 == m_classRefs )
00113 return error("Unable to load class description for ObjectRefs");
00114 return S_OK;
00115 }
00116
00117
00118 StatusCode RootCnvSvc::finalize() {
00119 log() << MSG::INFO;
00120 if ( m_ioMgr ) {
00121 IIODataManager::Connections cons = m_ioMgr->connections(0);
00122 for(IIODataManager::Connections::iterator i=cons.begin(); i != cons.end(); ++i) {
00123 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(*i);
00124 if ( pc ) {
00125 if ( pc->owner() == this && !m_ioPerfStats.empty() ) {
00126 pc->saveStatistics(m_ioPerfStats);
00127 }
00128 if ( pc->lookupClient(this) ) {
00129 size_t num_clients = pc->removeClient(this);
00130 if ( num_clients == 0 ) {
00131 if ( m_ioMgr->disconnect(pc).isSuccess() ) {
00132 log() << "Disconnected data IO:" << pc->fid()
00133 << " [" << pc->pfn() << "]" << endmsg;
00134 delete pc;
00135 }
00136 }
00137 }
00138 }
00139 }
00140 releasePtr(m_ioMgr);
00141 }
00142 deletePtr(m_log);
00143 releasePtr(m_incidentSvc);
00144 return ConversionSvc::finalize();
00145 }
00146
00147
00148 IConverter* RootCnvSvc::createConverter(long typ,const CLID& wanted,const ICnvFactory*) {
00149 if ( wanted == CLID_StatisticsFile )
00150 return new RootDatabaseCnv(typ,wanted,serviceLocator().get(),this);
00151 else if ( wanted == CLID_StatisticsDirectory )
00152 return new RootDirectoryCnv(typ,wanted,serviceLocator().get(),this);
00153 else if ( wanted == CLID_RowWiseTuple )
00154 return new RootNTupleCnv(typ,wanted,serviceLocator().get(),this);
00155 else if ( wanted == CLID_ColumnWiseTuple )
00156 return new RootNTupleCnv(typ,wanted,serviceLocator().get(),this);
00157 else
00158 return new RootConverter(typ,wanted,serviceLocator().get(),this);
00159 }
00160
00161
00162 void RootCnvSvc::loadConverter(DataObject* pObject) {
00163 if (pObject) {
00164 string cname = System::typeinfoName(typeid(*pObject));
00165 log() << MSG::DEBUG << "Trying to 'Autoload' dictionary for class " << cname << endmsg;
00166 TClass* cl = s_classesNames[cname];
00167 if ( 0 == cl ) {
00168 cl = gROOT->GetClass(cname.c_str());
00169 if ( cl ) {
00170 s_classesNames[cname] = cl;
00171 s_classesClids[pObject->clID()] = cl;
00172 }
00173 }
00174 }
00175 }
00176
00177
00178 TClass* RootCnvSvc::getClass(DataObject* pObject) {
00179 map<CLID, TClass*>::iterator i=s_classesClids.find(pObject->clID());
00180 if ( i != s_classesClids.end() ) return (*i).second;
00181 loadConverter(pObject);
00182 i=s_classesClids.find(pObject->clID());
00183 if ( i != s_classesClids.end() ) return (*i).second;
00184
00185 string cname = System::typeinfoName(typeid(*pObject));
00186 throw runtime_error("Unknown ROOT class for object:"+cname);
00187 return 0;
00188 }
00189
00190
00191 StatusCode RootCnvSvc::connectOutput(CSTR dsn, CSTR openMode) {
00192 StatusCode sc = S_FAIL;
00193 m_current = 0;
00194 m_currSection = "";
00195 if ( ::strncasecmp(openMode.c_str(),"RECREATE",3)==0 )
00196 sc = connectDatabase(dsn, IDataConnection::RECREATE, &m_current);
00197 else if ( ::strncasecmp(openMode.c_str(),"NEW",1)==0 )
00198 sc = connectDatabase(dsn, IDataConnection::CREATE, &m_current);
00199 else if ( ::strncasecmp(openMode.c_str(),"CREATE",1)==0 )
00200 sc = connectDatabase(dsn, IDataConnection::CREATE, &m_current);
00201 else if ( ::strncasecmp(openMode.c_str(),"UPDATE",1)==0 )
00202 sc = connectDatabase(dsn, IDataConnection::UPDATE, &m_current);
00203 if ( sc.isSuccess() && m_current && m_current->isConnected() ) {
00204 return S_OK;
00205 }
00206 m_incidentSvc->fireIncident(Incident(dsn,IncidentType::FailOutputFile));
00207 log() << MSG::ERROR << "The dataset " << dsn << " cannot be opened in mode "
00208 << openMode << ". [Invalid mode]" << endmsg;
00209 return sc;
00210 }
00211
00212
00213 StatusCode
00214 RootCnvSvc::connectDatabase(CSTR dataset, int mode, RootDataConnection** con) {
00215 try {
00216 IDataConnection* c = m_ioMgr->connection(dataset);
00217 bool fire_incident = false;
00218 *con = 0;
00219 if ( !c ) {
00220 auto_ptr<IDataConnection> connection(new RootDataConnection(this,dataset,m_setup));
00221 StatusCode sc = (mode != IDataConnection::READ)
00222 ? m_ioMgr->connectWrite(connection.get(),IDataConnection::IoType(mode),"ROOT")
00223 : m_ioMgr->connectRead(false,connection.get());
00224 c = sc.isSuccess() ? m_ioMgr->connection(dataset) : 0;
00225 if ( c ) {
00226 fire_incident = m_incidentEnabled && (0 != (mode&(IDataConnection::UPDATE|IDataConnection::READ)));
00227 if ( 0 != (mode&IDataConnection::READ) ) {
00228 if ( !m_ioPerfStats.empty() ) {
00229 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(c);
00230 pc->enableStatistics(m_setup->loadSection);
00231 }
00232 }
00233 connection.release();
00234 }
00235 else {
00236 m_incidentSvc->fireIncident(Incident(dataset,mode == IDataConnection::READ
00237 ? IncidentType::FailInputFile
00238 : IncidentType::FailOutputFile));
00239 return error("Cannot open data file:"+dataset);
00240 }
00241 }
00242 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(c);
00243 if ( pc ) {
00244 if ( !pc->isConnected() ) pc->connectRead();
00245 *con = pc;
00246 pc->resetAge();
00247 pc->addClient(this);
00248 }
00249 if ( *con ) {
00250 if ( fire_incident ) {
00251 IOpaqueAddress* pAddr = 0;
00252 string fid = pc->fid();
00253 string section = m_recordName[0] == '/' ? m_recordName.substr(1) : m_recordName;
00254 TBranch* b = pc->getBranch(section,m_recordName);
00255 log() << MSG::VERBOSE;
00256 if ( b ) {
00257 const string par[2] = { fid, m_recordName };
00258 unsigned long ipar[2] = { (unsigned long)(*con), (unsigned long)b->GetEntries()-1 };
00259 for(int i=0; i<b->GetEntries(); ++i) {
00260 ipar[1] = i;
00261 if ( !pc->mergeFIDs().empty() )
00262 fid = pc->mergeFIDs()[i];
00263 if ( !createAddress(repSvcType(),CLID_DataObject,par,ipar,pAddr).isSuccess() ) {
00264 log() << "Failed to create address for " << m_recordName << " in:" << fid
00265 << " [" << pc->fid() << "][" << i << "]" << endmsg;
00266 continue;
00267 }
00268 log() << "Prepare " << m_recordName << " " << fid << " [" << par[0] << "][" << i << "]" << endmsg;
00269 m_incidentSvc->fireIncident(ContextIncident<IOpaqueAddress*>(fid,"FILE_OPEN_READ",pAddr));
00270 }
00271 }
00272 else {
00273 log() << "No valid Records " << m_recordName << " present in:" << pc->fid() << endmsg;
00274 }
00275 }
00276
00277 IIODataManager::Connections cons = m_ioMgr->connections(this);
00278 for(IIODataManager::Connections::iterator i=cons.begin(); i != cons.end(); ++i) {
00279 if ( (*i) != *con && !(*i)->isConnected() ) {
00280 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(*i);
00281 if ( pc && pc->lookupClient(this) ) {
00282 size_t num_client = pc->removeClient(this);
00283 if ( num_client == 0 ) {
00284 if ( m_ioMgr->disconnect(pc).isSuccess() ) {
00285 log() << MSG::INFO << "Removed disconnected IO stream:" << pc->fid()
00286 << " [" << pc->pfn() << "]" << endmsg;
00287 delete pc;
00288 }
00289 }
00290 }
00291 }
00292 }
00293 return S_OK;
00294 }
00295 m_incidentSvc->fireIncident(Incident(dataset,IncidentType::FailOutputFile));
00296 return S_FAIL;
00297 }
00298 catch (exception& e) {
00299 m_incidentSvc->fireIncident(Incident(dataset,IncidentType::FailOutputFile));
00300 return error(string("connectDatabase> Caught exception:")+e.what());
00301 }
00302 catch (...) {
00303 m_incidentSvc->fireIncident(Incident(dataset,IncidentType::FailOutputFile));
00304 return error("connectDatabase> Unknown Fatal Exception for "+dataset);
00305 }
00306 }
00307
00308
00309 StatusCode RootCnvSvc::connectOutput(CSTR db_name) {
00310 return connectOutput(db_name, "NEW");
00311 }
00312
00313
00314 StatusCode RootCnvSvc::commitOutput(CSTR dsn, bool ) {
00315 if ( m_current ) {
00316 size_t len = m_currSection.find('/',1);
00317 string section = m_currSection.substr(1,len==string::npos ? string::npos : len-1);
00318 TBranch* b = m_current->getBranch(section, m_currSection);
00319 if ( b ) {
00320 Long64_t evt = b->GetEntries();
00321 TTree* t = b->GetTree();
00322 TObjArray* a = t->GetListOfBranches();
00323 Int_t nb = a->GetEntriesFast();
00324 log() << MSG::DEBUG;
00326 for(Int_t i=0; i<nb; ++i) {
00327 TBranch* br_ptr = (TBranch*)a->UncheckedAt(i);
00328 Long64_t br_evt = br_ptr->GetEntries();
00329 if ( br_evt < evt ) {
00330 Long64_t num = evt-br_evt;
00331 br_ptr->SetAddress(0);
00332 while(num>0) { br_ptr->Fill(); --num; }
00333 log() << "commit: Added " << long(evt-br_evt)
00334 << " Section: " << evt << " Branch: " << br_ptr->GetEntries()
00335 << " RefNo: " << br_ptr->GetEntries()-1
00336 << " NULL entries to:" << br_ptr->GetName() << endmsg;
00337 }
00338 }
00339
00340 b->GetTree()->SetEntries(evt);
00341 if ( evt == 1 ) {
00342 b->GetTree()->OptimizeBaskets(m_setup->basketSize,1.1,"");
00343 }
00344 if ( evt > 0 && (evt%m_setup->autoFlush)==0 ) {
00345 if ( evt == m_setup->autoFlush ) {
00346 b->GetTree()->SetAutoFlush(m_setup->autoFlush);
00347 b->GetTree()->OptimizeBaskets(m_setup->basketSize,1.,"");
00348 }
00349 else {
00350 b->GetTree()->FlushBaskets();
00351 }
00352 }
00353 log() << MSG::DEBUG << "Set section entries of " << m_currSection
00354 << " to " << long(evt) << " entries." << endmsg;
00355 }
00356 else {
00357 return error("commitOutput> Failed to update entry numbers on "+dsn);
00358 }
00359 }
00360 return S_OK;
00361 }
00362
00363
00364 StatusCode RootCnvSvc::disconnect(CSTR dataset) {
00365 IDataConnection* c = m_ioMgr->connection(dataset);
00366 return c ? m_ioMgr->disconnect(c) : S_FAIL;
00367 }
00368
00369
00370 StatusCode RootCnvSvc::createAddress(long typ,
00371 const CLID& clid,
00372 const string* par,
00373 const unsigned long* ip,
00374 IOpaqueAddress*& refpAddress)
00375 {
00376 refpAddress = new RootAddress(typ,clid,par[0],par[1],ip[0],ip[1]);
00377 return S_OK;
00378 }
00379
00380
00381 StatusCode RootCnvSvc::createNullRep(const std::string& path) {
00382 size_t len = path.find('/',1);
00383 string section = path.substr(1,len==string::npos ? string::npos : len-1);
00384 m_current->saveObj(section,path,0,0);
00385 return S_OK;
00386 }
00387
00388
00389 StatusCode RootCnvSvc::createNullRef(const std::string& path) {
00390 RootObjectRefs* refs = 0;
00391 size_t len = path.find('/',1);
00392 string section = path.substr(1,len==string::npos ? string::npos : len-1);
00393 pair<int,unsigned long> ret = m_current->save(section,path+"#R",0,refs);
00394 log() << MSG::VERBOSE << "Writing object:" << path << " "
00395 << ret.first << " " << hex << ret.second << dec << " [NULL]" << endmsg;
00396 return S_OK;
00397 }
00398
00399
00400 StatusCode RootCnvSvc::i__createRep(DataObject* pObj, IOpaqueAddress*& refpAddr) {
00401 refpAddr = 0;
00402 if ( pObj ) {
00403 CLID clid = pObj->clID();
00404 IRegistry* pR = pObj->registry();
00405 string p[2] = {m_current->fid(), pR->identifier()};
00406 TClass* cl = (clid == CLID_DataObject) ? m_classDO : getClass(pObj);
00407 size_t len = p[1].find('/',1);
00408 string sect = p[1].substr(1,len==string::npos ? string::npos : len-1);
00409 pair<int,unsigned long> ret = m_current->saveObj(sect,p[1],cl,pObj,true);
00410 if ( ret.first > 1 || (clid == CLID_DataObject && ret.first==1) ) {
00411 unsigned long ip[2] = {0,ret.second};
00412 if ( m_currSection.empty() ) m_currSection = p[1];
00413 return createAddress(repSvcType(),clid,p,ip,refpAddr);
00414 }
00415 return error("Failed to write object data for:"+p[1]);
00416 }
00417 return error("createRep> Current Database is invalid!");
00418 }
00419
00420
00421 StatusCode RootCnvSvc::i__fillRepRefs(IOpaqueAddress* , DataObject* pObj) {
00422 if ( pObj ) {
00423 typedef vector<IRegistry*> Leaves;
00424 Leaves leaves;
00425 RootObjectRefs refs;
00426 IRegistry* pR = pObj->registry();
00427 SmartIF<IDataManagerSvc> dataMgr(pR->dataSvc());
00428 if ( dataMgr ) {
00429 StatusCode status = dataMgr->objectLeaves(pObj, leaves);
00430 if ( status.isSuccess() ) {
00431 RootRef ref;
00432 const string& id = pR->identifier();
00433 size_t len = id.find('/',1);
00434 string sect = id.substr(1,len==string::npos ? string::npos : len-1);
00435 LinkManager* pLinks = pObj->linkMgr();
00436 for(Leaves::iterator i=leaves.begin(), iend=leaves.end(); i != iend; ++i) {
00437 if ( (*i)->address() ) {
00438 m_current->makeRef(*i,ref);
00439 ref.entry = (*i)->address()->ipar()[1];
00440 refs.refs.push_back(ref);
00441 }
00442 }
00443 for(int i = 0, n=pLinks->size(); i < n; ++i) {
00444 LinkManager::Link* lnk = pLinks->link(i);
00445 int link_id = m_current->makeLink(lnk->path());
00446 refs.links.push_back(link_id);
00447 }
00448 pair<int,unsigned long> ret = m_current->save(sect,id+"#R",m_classRefs,&refs,true);
00449 if ( ret.first > 1 ) {
00450 log() << MSG::DEBUG << "Writing object:" << id << " "
00451 << ret.first << " " << hex << ret.second << dec << endmsg;
00452 return S_OK;
00453 }
00454 }
00455 }
00456 }
00457 return S_FAIL;
00458 }
00459
00460
00461 StatusCode RootCnvSvc::i__createObj(IOpaqueAddress* pA, DataObject*& refpObj) {
00462 refpObj = 0;
00463 if ( pA ) {
00464 RootDataConnection* con = 0;
00465 const string* par = pA->par();
00466 unsigned long* ipar = const_cast<unsigned long*>(pA->ipar());
00467 StatusCode sc = connectDatabase(par[0],IDataConnection::READ,&con);
00468 if ( sc.isSuccess() ) {
00469 ipar[0] = (unsigned long)con;
00470 DataObject* pObj = 0;
00471 size_t len = par[1].find('/',1);
00472 string section = par[1].substr(1,len==string::npos ? string::npos : len-1);
00473
00474 int nb = con->loadObj(section,par[1],ipar[1],pObj);
00475 if ( nb > 1 || (nb == 1 && pObj->clID() == CLID_DataObject) ) {
00476 refpObj = pObj;
00477 return S_OK;
00478 }
00479 delete pObj;
00480 }
00481 string tag = par[0]+":"+par[1];
00482 if ( m_badFiles.find(tag) == m_badFiles.end() ) {
00483 m_badFiles.insert(tag);
00484 return error("createObj> Cannot access the object:"+tag);
00485 }
00486 return S_FAIL;
00487 }
00488 return S_FAIL;
00489 }
00490
00491
00492 StatusCode RootCnvSvc::i__fillObjRefs(IOpaqueAddress* pA, DataObject* pObj) {
00493 if ( pA && pObj ) {
00494 const unsigned long* ipar = pA->ipar();
00495 RootDataConnection* con = (RootDataConnection*)ipar[0];
00496 if ( con ) {
00497 RootObjectRefs refs;
00498 const string* par = pA->par();
00499 size_t len = par[1].find('/',1);
00500 string section = par[1].substr(1,len==string::npos ? string::npos : len-1);
00501 int nb = con->loadRefs(section,par[1],ipar[1],refs);
00502 log() << MSG::VERBOSE;
00503 if ( nb >= 1 ) {
00504 string npar[3];
00505 unsigned long nipar[2];
00506 IOpaqueAddress* nPA;
00507 IRegistry* pR = pObj->registry();
00508 SmartIF<IService> isvc(pR->dataSvc());
00509 SmartIF<IDataManagerSvc> dataMgr(pR->dataSvc());
00510 LinkManager* mgr = pObj->linkMgr();
00511 bool active = log().isActive();
00512 for(vector<int>::const_iterator i=refs.links.begin(); i!=refs.links.end();++i) {
00513 mgr->addLink(con->getLink(*i),0);
00514 }
00515 for(size_t j=0, n=refs.refs.size(); j<n; ++j) {
00516 const RootRef& r = refs.refs[j];
00517 npar[0] = con->getDb(r.dbase);
00518 npar[1] = con->getCont(r.container);
00519 npar[2] = con->getLink(r.link);
00520 nipar[0] = 0;
00521 nipar[1] = r.entry;
00522 StatusCode sc = addressCreator()->createAddress(r.svc,r.clid,npar,nipar,nPA);
00523 if ( sc.isSuccess() ) {
00524 if ( active ) {
00525 log() << isvc->name() << " -> Register:" << pA->registry()->identifier()
00526 << "#" << npar[2] << "[" << r.entry << "]" << endmsg;
00527 }
00528 sc = dataMgr->registerAddress(pA->registry(),npar[2],nPA);
00529 if ( sc.isSuccess() ) {
00530 continue;
00531 }
00532 }
00533 log() << MSG::ERROR << con->fid() << ": Failed to create address!!!!" << endmsg;
00534 return S_FAIL;
00535 }
00536 return pObj->update();
00537 }
00538 }
00539 return S_FAIL;
00540 }
00541 return error("read> Cannot read object -- no valid object address present ");
00542 }