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