00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #define GAUDISVC_NTUPLESVC_CPP
00023
00024
00025 #include "GaudiKernel/xtoa.h"
00026 #include "GaudiKernel/SmartIF.h"
00027 #include "GaudiKernel/Tokenizer.h"
00028 #include "GaudiKernel/SvcFactory.h"
00029 #include "GaudiKernel/DataObject.h"
00030 #include "GaudiKernel/ObjectFactory.h"
00031 #include "GaudiKernel/GenericAddress.h"
00032
00033 #include "GaudiKernel/IProperty.h"
00034 #include "GaudiKernel/ISvcLocator.h"
00035 #include "GaudiKernel/IDataSelector.h"
00036
00037 #include "GaudiKernel/Property.h"
00038 #include "GaudiKernel/Selector.h"
00039 #include "GaudiKernel/MsgStream.h"
00040 #include "GaudiKernel/ConversionSvc.h"
00041 #include "GaudiKernel/DataSelectionAgent.h"
00042 #include "GaudiKernel/NTupleImplementation.h"
00043
00044 #include "NTupleSvc.h"
00045
00046
00047
00048 DECLARE_SERVICE_FACTORY(NTupleSvc)
00049
00050
00051 DECLARE_NAMESPACE_OBJECT_FACTORY(NTuple,Selector)
00052
00053 #include <sstream>
00054
00055 namespace {
00056 inline std::string toStr(long id) {
00057 std::ostringstream s; s << id;
00058 return s.str();
00059 }
00060 }
00061
00063 NTupleSvc::NTupleSvc(const std::string& name, ISvcLocator* svc)
00064 : base_class(name, svc)
00065 {
00066 declareProperty("Input", m_input);
00067 declareProperty("Output", m_output);
00068 m_rootName = "/NTUPLES";
00069 m_rootCLID = CLID_DataObject;
00070 }
00071
00073 NTupleSvc::~NTupleSvc() {
00074 }
00075
00077 StatusCode NTupleSvc::initialize() {
00078 StatusCode status = DataSvc::initialize();
00079 if ( status.isSuccess() ) {
00080 status = setProperties();
00081 if ( status.isSuccess() ) {
00082 StatusCode iret(StatusCode::SUCCESS,true);
00083 DataObject* root = new NTuple::Directory();
00084 status = setRoot(m_rootName, root);
00085 for ( DBaseEntries::iterator i = m_output.begin(); i != m_output.end(); ++i ) {
00086 iret = connect(*i);
00087 if ( !iret.isSuccess() ) {
00088 status = iret;
00089 }
00090 }
00091 for ( DBaseEntries::iterator j = m_input.begin(); j != m_input.end(); ++j ) {
00092 iret = connect(*j);
00093 if ( !iret.isSuccess() ) {
00094 status = iret;
00095 }
00096 }
00097 }
00098 }
00099 return status;
00100 }
00101
00103 StatusCode NTupleSvc::reinitialize() {
00104 return StatusCode::SUCCESS;
00105 }
00106
00107
00108 bool NTupleSvc::isConnected(const std::string& identifier) const {
00109 Connections::const_iterator i = m_connections.find(identifier);
00110 return !(i==m_connections.end());
00111 }
00112
00114 IConversionSvc* NTupleSvc::getDataLoader(IRegistry* pRegistry) {
00115 if ( 0 != pRegistry ) {
00116 std::string full = pRegistry->identifier();
00117 size_t len = m_rootName.length();
00118 size_t idx = full.find(SEPARATOR,len+1);
00119 std::string path = (idx==std::string::npos) ? full : full.substr(0, idx);
00120 Connections::iterator i = m_connections.find(path);
00121 if ( i != m_connections.end() ) {
00122 return (*i).second.service;
00123 }
00124 }
00125 return 0;
00126 }
00127
00128 StatusCode NTupleSvc::updateDirectories() {
00129 typedef std::vector<IRegistry*> Leaves;
00130 long need_update = 0;
00131 DataObject* pO = 0;
00132 StatusCode iret = findObject(m_rootName, pO);
00133 MsgStream log ( msgSvc(), name() );
00134
00135 if ( iret.isSuccess() ) {
00136 Leaves leaves;
00137 iret = objectLeaves(pO, leaves);
00138 if ( iret.isSuccess() ) {
00139
00140 for ( Leaves::iterator d = leaves.begin(); d != leaves.end(); d++ ) {
00141 if ( (*d)->object() ) {
00142 IOpaqueAddress* pA = (*d)->address();
00143 if ( pA ) {
00144 unsigned long typ = pA->ipar()[1];
00145 if ( typ == 'R' || typ == 'N' || typ == 'U' ) {
00146
00147 IConversionSvc* svc = getDataLoader(*d);
00148 if ( 0 != svc ) {
00149 StatusCode status;
00150 DataSelectionAgent agent;
00151 IDataSelector* sel = agent.selectedObjects();
00152 traverseSubTree ( (*d)->object(), &agent ).ignore();
00153 for(int i = sel->size()-1; i >= 0; i-- ) {
00154 DataObject* o = (*sel)[i];
00155 IRegistry* r = o->registry();
00156 status = svc->updateRep(r->address(), o);
00157 if ( !status.isSuccess() ) {
00158 iret = status;
00159 }
00160 }
00161 for(int j = sel->size()-1; j >= 0; j-- ) {
00162 DataObject* o = (*sel)[j];
00163 IRegistry* r = o->registry();
00164 status = svc->updateRepRefs(r->address(), o);
00165 if ( !status.isSuccess() ) {
00166 iret = status;
00167 }
00168 }
00169 if ( iret.isSuccess() ) need_update += sel->size();
00170 }
00171 }
00172 }
00173 }
00174 }
00175 }
00176 }
00177 if ( !iret.isSuccess() ) {
00178 log << MSG::ERROR << "ERROR while saving NTuples" << endmsg;
00179 return iret;
00180 }
00181 else if ( need_update > 0 ) {
00182 log << MSG::INFO << "NTuples saved successfully" << endmsg;
00183 }
00184 return iret;
00185 }
00186
00187
00188 void NTupleSvc::releaseConnection(Connection& c) {
00189 SmartIF<IService> isvc( c.service );
00190 if ( isvc.isValid( ) ) {
00191 isvc->finalize().ignore();
00192 }
00193 c.service->release();
00194 c.service = 0;
00195 }
00196
00197
00198 StatusCode NTupleSvc::disconnect(const std::string& nam) {
00199 Connections::iterator i = m_connections.find(nam);
00200 if ( i != m_connections.end() ) {
00201 releaseConnection((*i).second);
00202 m_connections.erase(i);
00203 return StatusCode::SUCCESS;
00204 }
00205 return StatusCode::FAILURE;
00206 }
00207
00208
00209 StatusCode NTupleSvc::disconnectAll() {
00210 for(Connections::iterator i = m_connections.begin(); i != m_connections.end(); ++i) {
00211 releaseConnection((*i).second);
00212 }
00213 m_connections.erase(m_connections.begin(), m_connections.end());
00214 return StatusCode::SUCCESS;
00215 }
00216
00218 StatusCode NTupleSvc::finalize() {
00219 StatusCode status = updateDirectories();
00220 status = clearStore();
00221 status = DataSvc::finalize();
00222 status = disconnectAll();
00223 return status;
00224 }
00225
00226 StatusCode NTupleSvc::connect(const std::string& ident) {
00227 std::string logName;
00228 return connect(ident, logName);
00229 }
00230
00231 StatusCode NTupleSvc::connect(const std::string& ident, std::string& logname) {
00232 MsgStream log ( msgSvc(), name() );
00233 DataObject* pO = 0;
00234 StatusCode status = findObject(m_rootName, pO);
00235 if ( status.isSuccess() ) {
00236 char typ=0;
00237 Tokenizer tok(true);
00238 std::vector<Prop> props;
00239 long loc = ident.find(" ");
00240 std::string filename, auth, svc = "", db_typ = "";
00241 logname = ident.substr(0,loc);
00242 tok.analyse(ident.substr(loc+1,ident.length()), " ", "", "", "=", "'", "'");
00243 for ( Tokenizer::Items::iterator i = tok.items().begin(); i != tok.items().end(); ++i) {
00244 const std::string& tag = (*i).tag();
00245 switch( ::toupper(tag[0]) ) {
00246 case 'A':
00247 break;
00248 case 'F':
00249 case 'D':
00250 filename = (*i).value();
00251 break;
00252 case 'O':
00253 switch( ::toupper((*i).value()[0]) ) {
00254 case 'C':
00255 case 'N':
00256 case 'W':
00257 typ = 'N';
00258 break;
00259 case 'U':
00260 typ = 'U';
00261 break;
00262 case 'O':
00263 case 'R':
00264 typ = 'O';
00265 break;
00266 default:
00267 typ = 0;
00268 break;
00269 }
00270 break;
00271 case 'T':
00272 db_typ = (*i).value();
00273 break;
00274 default:
00275 props.push_back( Prop((*i).tag(), (*i).value()));
00276 break;
00277 }
00278 }
00279 if ( 0 != typ ) {
00280 IConversionSvc* pSvc = 0;
00281 status = createService(name()+'.'+logname, db_typ, props, pSvc);
00282 if ( status.isSuccess() ) {
00283 status = attachTuple(filename, logname, typ, pSvc->repSvcType());
00284 if ( status.isSuccess() ) {
00285 m_connections.insert(Connections::value_type(m_rootName+'/'+logname,Connection(pSvc)));
00286 return StatusCode::SUCCESS;
00287 }
00288 }
00289 }
00290 }
00291 log << MSG::ERROR << "Cannot add " << ident << " invalid filename!" << endmsg;
00292 return StatusCode::FAILURE;
00293 }
00294
00295 StatusCode NTupleSvc::createService(const std::string& ,
00296 const std::string& typ,
00297 const std::vector<Prop>& ,
00298 IConversionSvc*& pSvc)
00299 {
00300 MsgStream log ( msgSvc(), name() );
00302
00303 IProperty* appPropMgr = 0;
00304 StatusCode sts = serviceLocator()->queryInterface(IProperty::interfaceID(), pp_cast<void>(&appPropMgr) );
00305 if( !sts.isSuccess() ) {
00306
00307 log << MSG::ERROR << "Could not get PropMgr" << endmsg;
00308 return sts;
00309 }
00310
00311 StringProperty sp("HistogramPersistency","");
00312 sts = appPropMgr->getProperty( &sp );
00313 if ( !sts.isSuccess() ) {
00314 log << MSG::ERROR << "Could not get NTuple Persistency format"
00315 << " from ApplicationMgr properties" << endmsg;
00316 return sts;
00317 }
00318
00319 long storage_typ = TEST_StorageType;
00320 if ( sp.value() == "HBOOK" ) {
00321 storage_typ = HBOOK_StorageType;
00322 }
00323 else if ( sp.value() == "ROOT" ) {
00324 storage_typ = ROOT_StorageType;
00325 }
00326 else {
00327 appPropMgr->release();
00328 log << MSG::ERROR << "Unknown NTuple Persistency format: " << sp.value() << endmsg;
00329 return StatusCode::FAILURE;
00330 }
00331
00332 appPropMgr->release();
00333
00334 if ( typ.length() > 0 && typ != sp.value() ) {
00335 log << MSG::WARNING << "NTuple persistency type is "
00336 << sp.value() << "." << endmsg
00337 << "Type given by job option "
00338 << "NTupleSvc.Input/Output ignored!" << endmsg;
00339 }
00340
00341
00342
00343
00344 IService* pService = 0;
00345 IInterface* iface = new ConversionSvc(name()+"Conversions", serviceLocator(), storage_typ);
00346 StatusCode status = iface->queryInterface(IService::interfaceID(), pp_cast<void>(&pService));
00347 if ( status.isSuccess() ) {
00348 status = iface->queryInterface(IConversionSvc::interfaceID(), pp_cast<void>(&pSvc));
00349 if ( !status.isSuccess() ) {
00350 pService->release();
00351 return status;
00352 }
00353 }
00354 status = pService->sysInitialize();
00355 if ( !status.isSuccess() ) {
00356 return status;
00357 }
00358 pService->release();
00359 status = pSvc->setDataProvider(this);
00360 if ( !status.isSuccess() ) {
00361 return status;
00362 }
00363 return status;
00364 }
00365
00367 StatusCode NTupleSvc::create(const CLID& typ, const std::string& title, NTuple::Tuple*& refpTuple) {
00368 NTuple::TupleImp* pTuple = 0;
00369 StatusCode status = StatusCode::FAILURE;
00370 if ( typ == CLID_ColumnWiseTuple ) {
00371 pTuple = new NTuple::ColumnWiseTuple( title );
00372 }
00373 else if ( typ == CLID_RowWiseTuple ) {
00374 pTuple = new NTuple::RowWiseTuple( title );
00375 }
00376 else {
00378 }
00379 if ( 0 != pTuple ) {
00380 pTuple->setTupleService(this);
00381 status = StatusCode::SUCCESS;
00382 }
00383 refpTuple = pTuple;
00384 return status;
00385 }
00386
00388 NTuple::Tuple* NTupleSvc::book (const std::string& fullPath, const CLID& type, const std::string& title) {
00389 DataObject* pObj = 0;
00390 std::string path = fullPath;
00391 MsgStream log(msgSvc(), name());
00392 if ( path[0] != SEPARATOR ) {
00393 path = m_rootName;
00394 path += SEPARATOR;
00395 path += fullPath;
00396 }
00397 StatusCode status = retrieveObject(path, pObj);
00398 if ( !status.isSuccess() ) {
00399 int sep = path.rfind(SEPARATOR);
00400 if ( sep > 0 ) {
00401 std::string p_path (path, 0, sep);
00402 std::string o_path (path, sep, path.length());
00403 DataObject* dir = createDirectory(p_path);
00404 if ( 0 != dir ) {
00405 NTuple::Tuple* tup = book( dir, o_path, type, title);
00406 if ( 0 == tup ) {
00407 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Unknown reason)" << endmsg;
00408 }
00409 return tup;
00410 }
00411 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Invalid parent directory)" << endmsg;
00412 return 0;
00413 }
00414 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Invalid path)" << endmsg;
00415 return 0;
00416 }
00417 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Exists already)" << endmsg;
00418 return 0;
00419 }
00420
00422 NTuple::Tuple* NTupleSvc::book (const std::string& dirPath, const std::string& relPath, const CLID& type, const std::string& title) {
00423 std::string full = dirPath;
00424 if (relPath[0] != SEPARATOR) full += SEPARATOR;
00425 full += relPath;
00426 return book(full, type, title);
00427 }
00428
00430 NTuple::Tuple* NTupleSvc::book (const std::string& dirPath, long id, const CLID& type, const std::string& title) {
00431 return book( dirPath, toStr(id), type, title);
00432 }
00433
00435 NTuple::Tuple* NTupleSvc::book (DataObject* pParent, const std::string& relPath, const CLID& type, const std::string& title) {
00436 NTuple::Tuple* pObj = 0;
00437
00438 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj));
00439
00440 if ( !status.isSuccess() ) {
00441 status = create( type, title, pObj);
00442 if ( status.isSuccess() ) {
00443
00444 status = registerObject(pParent, relPath, pObj);
00445 if ( status.isSuccess() ) {
00446 return pObj;
00447 }
00448 pObj->release();
00449 }
00450 }
00451 return 0;
00452 }
00453
00455 NTuple::Tuple* NTupleSvc::book (DataObject* pParent,
00456 long id,
00457 const CLID& type,
00458 const std::string& title) {
00459 return book( pParent, toStr(id), type, title);
00460 }
00461
00463 NTuple::Directory* NTupleSvc::createDirectory (DataObject* pParent,
00464 const std::string& relPath) {
00465 if ( 0 != pParent ) {
00466 IRegistry* pDir = pParent->registry();
00467 if ( 0 != pDir ) {
00468 std::string full = pDir->identifier();
00469 full += (relPath[0]=='/') ? "" : "/";
00470 full += relPath;
00471 return createDirectory(full);
00472 }
00473 }
00474 return 0;
00475 }
00476
00478 NTuple::Directory* NTupleSvc::createDirectory (DataObject* pParent, long id) {
00479 return createDirectory( pParent, toStr(id) );
00480 }
00481
00483 NTuple::Directory* NTupleSvc::createDirectory (const std::string& dirPath, long id) {
00484 return createDirectory( dirPath, toStr(id) );
00485 }
00486
00488 NTuple::Directory* NTupleSvc::createDirectory (const std::string& dirPath, const std::string& relPath ) {
00489 std::string full = dirPath;
00490 full += (relPath[0]=='/') ? "" : "/";
00491 full += relPath;
00492 return createDirectory(full);
00493 }
00494
00495 StatusCode NTupleSvc::attachTuple(const std::string& filename, const std::string& logname, const char typ, const long t) {
00496 MsgStream log(msgSvc(), name());
00497 DataObject* p;
00498
00499 StatusCode status = retrieveObject(m_rootName, p);
00500 if ( status.isSuccess() ) {
00501
00502 std::string entryname = m_rootName;
00503 entryname += '/';
00504 entryname += logname;
00505 GenericAddress* pA =
00506 new GenericAddress(t, CLID_NTupleFile, filename, entryname, 0, typ);
00507 status = registerAddress(p, logname, pA);
00508 if ( status.isSuccess() ) {
00509 log << MSG::INFO << "Added stream file:" << filename << " as " << logname << endmsg;
00510 return status;
00511 }
00512 pA->release();
00513 }
00514 log << MSG::ERROR << "Cannot add file:" << filename << " as " << logname << endmsg;
00515 return status;
00516 }
00517
00519 NTuple::Directory* NTupleSvc::createDirectory (const std::string& fullPath) {
00520 NTuple::Directory* p = 0;
00521 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&p));
00522 if ( !status.isSuccess() ) {
00523 int sep2 = fullPath.rfind(SEPARATOR);
00524 if ( sep2 > 0 ) {
00525 std::string relPath = fullPath.substr(0, sep2);
00526 p = createDirectory(relPath);
00527 if ( 0 != p ) {
00528 p = new NTuple::Directory();
00529
00530 status = registerObject(fullPath, p);
00531 if ( status.isSuccess() ) {
00532
00533 IConversionSvc* svc = getDataLoader(p->registry());
00534 if ( 0 != svc ) {
00535 IOpaqueAddress* pAddr = 0;
00536 status = svc->createRep (p, pAddr);
00537 if ( status.isSuccess() ) {
00538 p->registry()->setAddress(pAddr);
00539 status = svc->fillRepRefs (pAddr, p);
00540 if ( status.isSuccess() ) {
00541 return p;
00542 }
00543 }
00544 }
00545 unregisterObject(p);
00546 }
00547 p->release();
00548 p = 0;
00549 }
00550 }
00551 }
00552 try {
00553 p = dynamic_cast<NTuple::Directory*>(p);
00554 return p;
00555 }
00556 catch (...) {
00557 }
00558 return 0;
00559 }
00560
00562 NTuple::Tuple* NTupleSvc::access(const std::string&, const std::string&) {
00563 MsgStream log ( msgSvc(), name() );
00564 return 0;
00565 }
00566
00568 StatusCode NTupleSvc::save(const std::string& fullPath) {
00569 MsgStream log ( msgSvc(), name() );
00570 NTuple::Tuple* pObj = 0;
00571 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&pObj));
00572 if ( status.isSuccess() ) {
00573 return save ( pObj );
00574 }
00575 return INVALID_OBJ_PATH;
00576 }
00577
00579 StatusCode NTupleSvc::save(NTuple::Tuple* n_tuple) {
00580 NTuple::TupleImp* tuple = (NTuple::TupleImp*)n_tuple;
00581 if ( 0 != tuple ) {
00582 try {
00583 IConversionSvc* pSvc = tuple->conversionService();
00584 IRegistry* pReg = tuple->registry();
00585 if ( 0 != pSvc && 0 != pReg ) {
00586 IOpaqueAddress* pAddr = pReg->address();
00587 StatusCode status = pSvc->updateRep(pAddr, n_tuple);
00588 if ( status.isSuccess() ) {
00589 status = pSvc->updateRepRefs(pAddr, n_tuple);
00590 }
00591 return status;
00592 }
00593 return IDataProviderSvc::NO_DATA_LOADER;
00594 }
00595 catch(...) {
00596 }
00597 }
00598 return INVALID_OBJECT;
00599 }
00600
00602 StatusCode NTupleSvc::save(DataObject* pParent, const std::string& relPath) {
00603 NTuple::Tuple* pObj = 0;
00604 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj));
00605 if ( status.isSuccess() ) {
00606 return save ( pObj );
00607 }
00608 return INVALID_OBJ_PATH;
00609 }
00610
00612 StatusCode NTupleSvc::writeRecord( NTuple::Tuple* n_tuple ) {
00613 NTuple::TupleImp* tuple = (NTuple::TupleImp*)n_tuple;
00614 if ( 0 != tuple ) {
00615 try {
00616 IConversionSvc* pSvc = tuple->conversionService();
00617 if ( 0 == pSvc ) {
00618 pSvc = getDataLoader(n_tuple->registry());
00619 tuple->setConversionService(pSvc);
00620 }
00621 if ( 0 != pSvc ) {
00622 IRegistry* pReg = n_tuple->registry();
00623 IOpaqueAddress* pAddr = pReg->address();
00624 StatusCode status = pSvc->createRep(n_tuple, pAddr);
00625 if ( status.isSuccess() ) {
00626 pReg->setAddress(pAddr);
00627 status = pSvc->fillRepRefs(pAddr, n_tuple);
00628 }
00629 return status;
00630 }
00631 return IDataProviderSvc::NO_DATA_LOADER;
00632 }
00633 catch(...) {
00634 }
00635 }
00636 return INVALID_OBJECT;
00637 }
00638
00640 StatusCode NTupleSvc::writeRecord(const std::string& fullPath ) {
00641 NTuple::Tuple* pObj = 0;
00642 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&pObj));
00643 if ( status.isSuccess() ) {
00644 return writeRecord ( pObj );
00645 }
00646 return INVALID_OBJ_PATH;
00647 }
00648
00650 StatusCode NTupleSvc::writeRecord( DataObject* pParent, const std::string& relPath) {
00651 NTuple::Tuple* pObj = 0;
00652 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj));
00653 if ( status.isSuccess() ) {
00654 return writeRecord ( pObj );
00655 }
00656 return INVALID_OBJ_PATH;
00657 }
00658
00660 StatusCode NTupleSvc::readRecord( NTuple::Tuple* n_tuple ) {
00661 StatusCode status = INVALID_OBJECT;
00662 NTuple::TupleImp* tuple = (NTuple::TupleImp*)n_tuple;
00663 if ( 0 != tuple ) {
00664 try {
00665 IConversionSvc* pSvc = tuple->conversionService();
00666 if ( 0 == pSvc ) {
00667 pSvc = getDataLoader(n_tuple->registry());
00668 tuple->setConversionService(pSvc);
00669 }
00670 if ( 0 != pSvc ) {
00671 IRegistry* pReg = n_tuple->registry();
00672 IOpaqueAddress* pAddr = pReg->address();
00673 status = pSvc->updateObj(pAddr, n_tuple);
00674 if ( status.isSuccess() ) {
00675 status = pSvc->updateObjRefs(pAddr, n_tuple);
00676 }
00677 return status;
00678 }
00679 status = IDataProviderSvc::NO_DATA_LOADER;
00680 }
00681 catch(...) {
00682 status = INVALID_OBJECT;
00683 }
00684 }
00685 return status;
00686 }
00687
00689 StatusCode NTupleSvc::readRecord(const std::string& fullPath) {
00690 NTuple::Tuple* pObj = 0;
00691 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&pObj));
00692 if ( status.isSuccess() ) {
00693 return readRecord ( pObj );
00694 }
00695 return INVALID_OBJ_PATH;
00696 }
00697
00699 StatusCode NTupleSvc::readRecord(DataObject* pParent, const std::string& relPath) {
00700 NTuple::Tuple* pObj = 0;
00701 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj));
00702 if ( status.isSuccess() ) {
00703 return readRecord ( pObj );
00704 }
00705 return INVALID_OBJ_PATH;
00706 }