util/Global.cpp

Go to the documentation of this file.
00001 /*------------------------------------------------------------------------------
00002 Name:      Global.cpp
00003 Project:   xmlBlaster.org
00004 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
00005 Comment:   Create unique timestamp
00006 Version:   $Id: Global.cpp 15992 2007-02-11 12:29:40Z ruff $
00007 ------------------------------------------------------------------------------*/
00008 #include <client/protocol/CbServerPluginManager.h>
00009 #include <util/dispatch/DispatchManager.h>
00010 #include <util/Timeout.h>
00011 #include <algorithm>
00012 #include <util/lexical_cast.h>
00013 #include <util/Global.h>
00014 
00015 // For usage():
00016 #include <util/qos/address/Address.h>
00017 #include <util/qos/address/CallbackAddress.h>
00018 #include <util/qos/storage/MsgUnitStoreProperty.h>
00019 #include <util/qos/storage/ClientQueueProperty.h>
00020 #include <util/qos/storage/CbQueueProperty.h>
00021 #if defined(XMLBLASTER_MSXML_PLUGIN)
00022 #  error Implement Microsoft XML parser for /DXMLBLASTER_MSXML_PLUGIN
00023 #else  // XMLBLASTER_XERCES_PLUGIN
00024 #  include <util/parser/Sax2XercesParser.h>
00025 #endif
00026 #ifdef COMPILE_SOCKET_PLUGIN
00027 #  include <client/protocol/socket/SocketDriver.h>
00028 #endif
00029 #ifdef COMPILE_CORBA_PLUGIN
00030 #  include <client/protocol/corba/CorbaDriver.h>
00031 #endif
00032 #ifdef XMLBLASTER_COMPILE_LOG4CPLUS_PLUGIN
00033 #  include<util/Log4cplus.h>
00034 #endif
00035 
00036 #if !defined(XMLBLASTER_NO_RCSID)
00037    /*
00038    Add the exact version of the C++ client library, this is for examination
00039    with for example the UNIX 'strings' command only.
00040    If it makes problem just set -DXMLBLASTER_NO_RCSID
00041    */
00042 #  if defined(__GNUC__) || defined(__ICC)
00043       // To support query state with 'ident libxmlBlasterClient.so' or 'what libxmlBlasterClient.so'
00044       // or 'strings libxmlBlasterClient.so  | grep Global.cpp'
00045       static const char *rcsid_GlobalCpp  __attribute__ ((unused)) =  "@(#) $Id: Global.cpp 15992 2007-02-11 12:29:40Z ruff $ xmlBlaster @version@ #@revision.number@";
00046 #  elif defined(__SUNPRO_CC)
00047       static const char *rcsid_GlobalCpp  =  "@(#) $Id: Global.cpp 15992 2007-02-11 12:29:40Z ruff $ xmlBlaster @version@ #@revision.number@";
00048 #  endif
00049 #endif
00050 
00051 namespace org { namespace xmlBlaster { namespace util {
00052 //#if __GNUC__ == 2 || defined(__sun)
00053 #if __GNUC__ == 2 || defined(__SUNPRO_CC)
00054 //#if __GNUC__ == 2
00055   // Problems with g++ 2.95.3 and template<>
00056 #else
00057 
00058 template<> std::string lexical_cast(bool arg)
00059 {
00060    return (arg) ? XMLBLASTER_TRUE : XMLBLASTER_FALSE; // "true", "false"
00061 }
00063 template<> const char * lexical_cast(bool arg)
00064 {
00065    return (arg) ? XMLBLASTER_TRUE.c_str() : XMLBLASTER_FALSE.c_str();
00066 }
00067 
00068 template<> bool lexical_cast(std::string arg)
00069 {
00070    return arg == "1" || arg == XMLBLASTER_TRUE || arg == "TRUE"; // "true"
00071 }
00072 
00073 template<> bool lexical_cast(const char* arg)
00074 {
00075    return lexical_cast<bool>(std::string(arg));
00076 }
00077 
00078 
00083 template<> std::string lexical_cast(std::string arg)
00084 {
00085    return arg;
00086 }
00087 
00088 #endif
00089 using namespace std;
00090 using namespace org::xmlBlaster::util::dispatch;
00091 using namespace org::xmlBlaster::client::protocol;
00092 
00093 Global::Global() : ME("Global"), pingerMutex_(), sessionName_(0)
00094 {
00095    cbServerPluginManager_ = 0;
00096    pingTimer_             = 0;
00097    dispatchManager_       = 0;
00098    copy();
00099    property_              = new Property();
00100    isInitialized_ = false;
00101 
00102    if(global_ == NULL)
00103      global_ = this;
00104 }
00105 
00106 void Global::copy()
00107 {
00108    args_        = 0 ;
00109    argv_        = NULL;
00110    property_    = NULL;
00111    pingTimer_   = NULL;
00112    dispatchManager_ = NULL;
00113    cbServerPluginManager_ = NULL;
00114    id_          = "";
00115 }
00116 
00117 /*
00118 Global::Global(const Global& global) : ME("Global")
00119 {
00120    args_ = global.args_;
00121    argv_ = global.argv_;
00122 }
00123 */
00124 
00125 Global& Global::operator =(const Global &)
00126 {
00127    copy();
00128    return *this;
00129 }
00130 
00131 
00132 Global::~Global()
00133 {
00134    try {
00135       delete property_;
00136       delete cbServerPluginManager_;
00137       delete pingTimer_;
00138       delete dispatchManager_;
00139    }
00140    catch (...) {
00141    }
00142    try {
00143       if (this != global_) {
00144          global_->destroyInstance(this->getInstanceName());
00145       }
00146       else {
00147          globalRefMap_.clear();
00148          globalMap_.clear();
00149       }
00150    }
00151    catch (...) {
00152    }
00153 }
00154 
00155 Global *Global::global_ = NULL;
00156 Global::GlobalRefMap Global::globalRefMap_;
00157 Global::GlobalMap Global::globalMap_;
00158 thread::Mutex Global::globalMutex_;
00159 
00160 //-----------------
00161 // Global.cpp modification
00162 Global& Global::getInstance(string name)
00163 {
00164    if (name == "" || name == "default") {
00165       if (global_ == NULL) {
00166          global_ = new Global();
00167          Object_Lifetime_Manager::instance()->manage_object(Constants::XB_GLOBAL_KEY, global_);  // if not pre-allocated.
00168       }
00169       return *global_;
00170    }
00171 
00172    GlobalMap::iterator iter = globalMap_.find(name);
00173    if (iter != globalMap_.end()) {
00174       Global* glP = (*iter).second;
00175       return *glP;
00176    }
00177 
00178    throw XmlBlasterException(USER_ILLEGALARGUMENT,
00179          "UNKNOWN NODE",
00180          string("Global::getInstance"),
00181             "The Global instance '" + name + "' is not known");
00182 }
00183 
00184 GlobalRef Global::createInstance(const string& name, const Property::MapType *propertyMapP, bool holdReferenceCount)
00185 {
00186    if (name == "") {
00187      throw XmlBlasterException(USER_ILLEGALARGUMENT,
00188                   "UNKNOWN NODE",
00189                   string("Global::createInstance"),
00190                    "Please call createInstance with none empty name argument");
00191    }
00192    if (name == "default") {
00193      throw XmlBlasterException(USER_ILLEGALARGUMENT,
00194                   "UNKNOWN NODE",
00195                   string("Global::createInstance"),
00196                    "Please call getInstance to access the 'default' Global instance");
00197    }
00198 
00199    thread::Lock lock(globalMutex_);
00200 
00201    {
00202       GlobalRefMap::iterator iter = globalRefMap_.find(name);
00203       if (iter != globalRefMap_.end()) {
00204          GlobalRef gr = (*iter).second;
00205          return gr;
00206       }
00207    }
00208    {
00209       GlobalMap::iterator iter = globalMap_.find(name);
00210       if (iter != globalMap_.end()) {
00211          //Global* glP = (*iter).second;
00212          throw XmlBlasterException(USER_ILLEGALARGUMENT,
00213                      "UNKNOWN NODE",
00214                      string("Global::createInstance"),
00215                       "Please call getInstance to access the '" + name + "' Global instance");
00216       }
00217    }
00218 
00219    if (holdReferenceCount) {
00220       GlobalRef globRef = GlobalRef(new Global());
00221       globRef->instanceName_ = name;
00222       if (propertyMapP != NULL) {
00223          globRef->initialize(*propertyMapP);
00224       }
00225       GlobalRefMap::value_type el(name, globRef);
00226       globalRefMap_.insert(el);
00227       return globRef;
00228    }
00229    else {
00230       Global* glP = new Global();
00231       glP->instanceName_ = name;
00232       if (propertyMapP != NULL) {
00233          glP->initialize(*propertyMapP);
00234       }
00235       GlobalMap::value_type el(name, glP);
00236       globalMap_.insert(el);
00237       return GlobalRef(glP);
00238    }
00239 }
00240 
00241 const string& Global::getInstanceName()
00242 {
00243    if (this->instanceName_ == "" && this == global_) {
00244       this->instanceName_ = "default";
00245    }
00246    return this->instanceName_;
00247 }
00248 
00249 bool Global::containsInstance(const std::string &name)
00250 {
00251    bool ret = globalRefMap_.count(name) != 0;
00252    if (ret) {
00253       return true;
00254    }
00255    return globalMap_.count(name) != 0;
00256 }
00257 
00258 bool Global::destroyInstance(const std::string &name)
00259 {
00260    if (name == "") {
00261       return false;
00262    }
00263    if (name == "default") {
00264      throw XmlBlasterException(USER_ILLEGALARGUMENT,
00265                   "UNKNOWN NODE",
00266                   string("Global::destroyInstance"),
00267                    "The 'default' Global instance is handled by the life time manager and can't be destroyed manually");
00268    }
00269    thread::Lock lock(globalMutex_);
00270    {
00271       bool ret = globalRefMap_.count(name) != 0;
00272       if (ret) {
00273          globalRefMap_.erase(name);
00274          //cout << "DEBUG ONLY: " << name << " RefSize=" + lexical_cast<std::string>(globalRefMap_.size()) <<
00275          //     " Size=" + lexical_cast<std::string>(globalMap_.size()) << endl;
00276          return ret;
00277       }
00278    }
00279    {
00280       bool ret = globalMap_.count(name) != 0;
00281       if (ret) {
00282          globalMap_.erase(name);
00283          //cout << "DEBUG ONLY: " << name << " Size=" + lexical_cast<std::string>(globalMap_.size()) <<
00284          //     " RefSize=" + lexical_cast<std::string>(globalRefMap_.size()) << endl;
00285          return ret;
00286       }
00287    }
00288    return false;
00289 }
00290 
00291 Global& Global::initialize(int args, const char * const argv[])
00292 {
00293    if (isInitialized_) {
00294       getLog("org.xmlBlaster.util").warn(ME, "::initialize: the global is already initialized. Ignoring this initialization");
00295       return *this;
00296    }
00297    args_     = args;
00298    argv_     = argv;
00299    if (property_ != NULL) delete property_;
00300    property_ = NULL;
00301    property_ = new Property(args, argv);
00302    property_->loadPropertyFile(); // load xmlBlaster.properties
00303    isInitialized_ = true;
00304    return *this;
00305 }
00306 
00307 Global& Global::initialize(const Property::MapType &propertyMap)
00308 {
00309    if (isInitialized_) {
00310       getLog("org.xmlBlaster.util").warn(ME, "::initialize: the global is already initialized. Ignoring this initialization");
00311       return *this;
00312    }
00313    args_     = 0;
00314    argv_     = 0;
00315    if (property_ != NULL) delete property_;
00316    property_ = NULL;
00317    property_ = new Property(propertyMap);
00318    property_->loadPropertyFile(); // load xmlBlaster.properties
00319    isInitialized_ = true;
00320    return *this;
00321 }
00322 
00323 void Global::fillArgs(ArgsStruct_T &args)
00324 {
00325    if (property_ == 0) {
00326       args.argc = 0;
00327       args.argv = 0;
00328       return;
00329    }
00330    const Property::MapType &prmap = property_->getPropertyMap();
00331    args.argc = 2*prmap.size()+1;
00332    args.argv = new char *[args.argc];
00333 
00334    string execName = (argv_ != 0 && args_ > 0) ? argv_[0] : "xmlBlasterClient";
00335    args.argv[0] = new char[execName.length()+1];
00336    strcpy(args.argv[0],execName.c_str());
00337    int i = 1;
00338    Property::MapType::const_iterator ipm;
00339    for (ipm = prmap.begin(); ipm != prmap.end(); ++ipm) {
00340       args.argv[i] = new char[(*ipm).first.size()+2];
00341       *(args.argv[i]) = '-';
00342       strcpy(args.argv[i]+1, (*ipm).first.c_str()); i++;
00343       args.argv[i] = new char[(*ipm).second.size()+1];
00344       strcpy(args.argv[i], (*ipm).second.c_str()); i++;
00345    }
00346 }
00347 
00348 void Global::freeArgs(ArgsStruct_T &args)
00349 {
00350    for (size_t i=0; i<args.argc; i++)
00351       delete [] args.argv[i];
00352    delete [] args.argv;
00353    args.argc = 0;
00354 }
00355 
00356 bool Global::wantsHelp()
00357 {
00358    return getProperty().getBoolProperty("help", false, false) ||
00359           getProperty().getBoolProperty("-help", false, false) ||
00360           getProperty().getBoolProperty("h", false, false) ||
00361           getProperty().getBoolProperty("?", false, false); 
00362 }
00363 
00364 string &Global::getVersion()
00365 {
00366    static string version = "@version@";  // is replaced by ant / build.xml to e.g. "0.901"
00367    return version;
00368 }
00369 
00370 string &Global::getRevisionNumber()
00371 {
00372    static string revisionNumber = "@revision.number@";  // is replaced by ant / build.xml to subversions revision number, e.g. "1207"
00373    if (revisionNumber.find("@",0) != 0 && revisionNumber!=string("${revision.number}"))
00374       return revisionNumber;
00375    return getVersion();
00376 }
00377 
00378 string &Global::getReleaseId()
00379 {
00380    if (Global::getVersion() == Global::getRevisionNumber())
00381       return Global::getVersion();
00382    static string releaseId = Global::getVersion() + " #" + Global::getRevisionNumber();
00383         return releaseId;
00384 }
00385 
00386 string &Global::getBuildTimestamp()
00387 {
00388    static string timestamp = "@build.timestamp@"; // is replaced by ant / build.xml to e.g. "03/20/2003 10:22 PM";
00389    return timestamp;
00390 }
00391 
00392 string& Global::getCompiler()
00393 {
00394    static string cppCompiler = "@cpp.compiler@"; // is replaced by ant / build.xml to e.g. "g++";
00395    return cppCompiler;
00396 }
00397 
00398 
00402 string& Global::getDefaultProtocol()
00403 {
00404 #  if COMPILE_SOCKET_PLUGIN
00405    static string defaultProtocol = Constants::SOCKET;
00406 #  elif COMPILE_CORBA_PLUGIN
00407    static string defaultProtocol = Constants::IOR;
00408 #  else
00409    log_.error(ME, "Missing protocol in getDefaultProtocol(), please set COMPILE_CORBA_PLUGIN or COMPILE_SOCKET_PLUGIN on compilation");
00410 #  endif
00411    return defaultProtocol;
00412 }
00413 
00414 Property& Global::getProperty() const
00415 {
00416    if (property_ == NULL)
00417      throw XmlBlasterException(USER_CONFIGURATION,
00418                                "UNKNOWN NODE",
00419                                ME + string("::getProperty"), "Please call initialize to init Property");
00420    return *property_;
00421 }
00422 
00423 string Global::usage()
00424 {
00425    string sb;
00426    sb += "\n";
00427    sb += "\nXmlBlaster C++ client " + Global::getReleaseId() + " compiled at " + Global::getBuildTimestamp() + " with " + Global::getCompiler();
00428    sb += "\n";
00429 //#  if COMPILE_SOCKET_PLUGIN && COMPILE_CORBA_PLUGIN
00430    sb += "\n   -protocol SOCKET | IOR";
00431    sb += "\n                       IOR for CORBA, SOCKET for our native protocol.";
00432    sb += "\n";
00433 //#  endif
00434 #  ifdef COMPILE_SOCKET_PLUGIN
00435       sb += org::xmlBlaster::client::protocol::socket::SocketDriver::usage();
00436       sb += "\n   -logLevel           ERROR | WARN | INFO | TRACE | DUMP [WARN]";
00437       sb += "\n                       NOTE: Switch on C++ logging simultaneously to see the traces";
00438       sb += "\n                             as the C logging is redirected to the C++ logging library\n";
00439       sb += "\n";
00440 #  endif
00441 #  ifdef COMPILE_CORBA_PLUGIN
00442       sb += org::xmlBlaster::client::protocol::corba::CorbaDriver::usage();
00443       sb += "\n";
00444 #  endif
00445 #  if defined(XMLBLASTER_MSXML_PLUGIN)
00446 #     error Implement Microsoft XML parser for /DXMLBLASTER_MSXML_PLUGIN
00447 #  else  // XMLBLASTER_XERCES_PLUGIN
00448    sb += org::xmlBlaster::util::parser::Sax2Parser::usage();
00449    sb += "\n";
00450 #  endif
00451    sb += org::xmlBlaster::util::qos::SessionQos::usage();
00452    sb += "\n";
00453    sb += org::xmlBlaster::util::qos::address::Address(Global::getInstance()).usage();
00454    sb += "\n";
00455    sb += org::xmlBlaster::util::qos::storage::ClientQueueProperty::usage();
00456    sb += "\n";
00457    //sb += org::xmlBlaster::util::qos::storage::MsgUnitStoreProperty::usage();
00458    //sb += "\n";
00459    sb += org::xmlBlaster::util::qos::address::CallbackAddress(Global::getInstance()).usage();
00460    sb += "\n";
00461    sb += org::xmlBlaster::util::qos::storage::CbQueueProperty::usage();
00462    sb += "\n";
00463    const I_Log& ll = getInstance().getLog();
00464    sb += ll.usage();
00465    return sb;
00466    /*
00467       StringBuffer sb = new StringBuffer(4028);
00468       sb.append(org.xmlBlaster.client.XmlBlasterAccess.usage(this));
00469       sb.append(logUsage());
00470       return sb.toString();
00471    */
00472 }
00473 
00474 LogManager& Global::getLogManager()
00475 {
00476    return logManager_;
00477 }
00478 
00479 I_Log& Global::getLog(const string &logName)
00480 {
00481    try {
00482 #     if XMLBLASTER_COMPILE_LOG4CPLUS_PLUGIN==1
00483          static bool first = true;
00484          if (first) {
00485             logManager_.setLogFactory("log4cplus", new Log4cplusFactory());
00486             logManager_.initialize(getProperty().getPropertyMap());
00487             first = false;
00488          }
00489          return logManager_.getLogFactory().getLog(logName);
00490 #     else
00491          return logManager_.getLogFactory().getLog(logName); // Use our default Log.cpp
00492 #     endif
00493    }
00494    catch(...) {
00495       throw XmlBlasterException(INTERNAL_UNKNOWN, "UNKNOWN NODE", ME + string("::getLog() failed to setup logging configuration"));
00496    }
00497 }
00498 
00499 int Global::getArgs()
00500 {
00501    return args_;
00502 }
00503 
00504 const char * const* Global::getArgc()
00505 {
00506    return argv_;
00507 }
00508 
00509 string Global::getLocalIP() const
00510 {
00511    // change this to a better way later ...
00512    return string("127.0.0.1");
00513 }
00514 
00515 string Global::getBootstrapHostname() const
00516 {
00517    return getProperty().getStringProperty(string("bootstrapHostname"), getLocalIP());
00518    /* URL:
00519    string bootstrapHostname = getProperty().getStringProperty(string("bootstrapHostname"), getLocalIP());
00520    int bootstrapPort = getProperty().getIntProperty(string("bootstrapPort"), Constants::XMLBLASTER_PORT);
00521    return "http://" + bootstrapHostname + ":" + lexical_cast<std::string>(bootstrapPort);
00522    */
00523 }
00524 
00525 string Global::getCbHostname() const
00526 {
00527 //   std::cout << "Global::getCbHostname implementation is not finished" << std::endl;
00528    return getProperty().getStringProperty(string("bootstrapHostname"), getLocalIP());
00529 }
00530 
00531 CbServerPluginManager& Global::getCbServerPluginManager()
00532 {
00533    if (cbServerPluginManager_ == NULL) {
00534       cbServerPluginManager_ = new CbServerPluginManager(*this);
00535    }
00536    return *cbServerPluginManager_;
00537 }
00538 
00539 DispatchManager& Global::getDispatchManager()
00540 {
00541    if (dispatchManager_ == NULL) {
00542       dispatchManager_ = new DispatchManager(*this);
00543    }
00544    return *dispatchManager_;
00545 }
00546 
00547 Timeout& Global::getPingTimer()
00548 {
00549    if (pingTimer_) return *pingTimer_;
00550    thread::Lock lock(pingerMutex_);
00551    { // this is synchronized. Test again if meanwhile it has been set ...
00552       getLog("org.xmlBlaster.util").trace(ME, "::getPingTimer: creating the singleton 'ping timer'");
00553       if (pingTimer_) return *pingTimer_;
00554       pingTimer_ = new Timeout(*this, string("ping timer"));
00555       return *pingTimer_;
00556    }
00557 }
00558 
00559 const string& Global::getBoolAsString(bool val)
00560 {
00561    return (val) ? XMLBLASTER_TRUE : XMLBLASTER_FALSE;
00562    // return lexical_cast<std::string>(val); 
00563    // gcc complains: warning: returning reference to temporary
00564 }
00565 
00566 void Global::setSessionName(SessionNameRef sessionName)
00567 {
00568    sessionName_ = sessionName;
00569 }
00570 
00571 SessionNameRef Global::getSessionName() const
00572 {
00573    return sessionName_;
00574 }
00575 
00576 
00583 string Global::getId() const
00584 {
00585    return id_;
00586 }
00587 
00588 string Global::getImmutableId() const
00589 {
00590    return immutableId_;
00591 }
00592 
00593 string Global::getStrippedId() const
00594 {
00595    return getStrippedString(id_);
00596 }
00597 
00598 string Global::getStrippedImmutableId() const
00599 {
00600    return getStrippedString(immutableId_);
00601 }
00602 
00603 string Global::getStrippedString(const string& text) const
00604 {
00605    string ret = text;
00606    string::iterator
00607      ref = remove(ret.begin(), ret.end(), '/'); // StringHelper.replaceAll(text, "/", "");
00608    replace(ret.begin(), ref, '.', '_');         // StringHelper.replaceAll(strippedId, ".", "_");
00609    replace(ret.begin(), ref, ':', '_');         // StringHelper.replaceAll(strippedId, ":", "_");
00610    ref = remove(ret.begin(), ref, '\\');        // StringHelper.replaceAll(strippedId, "\\", "");
00611    ret.erase(ref, ret.end());
00612    return ret;
00613 }       
00614 
00615 void Global::setId(const string& id) 
00616 {
00617    id_ = id;
00618    resetInstanceId();
00619 }
00620 
00621 void Global::setImmutableId(const string& id) 
00622 {
00623    immutableId_ = id;
00624 }
00625 
00626 void Global::resetInstanceId() {
00627    thread::Lock lock(globalMutex_);
00628    instanceId_ = "";
00629 }
00630 
00631 std::string Global::getInstanceId() const {
00632    if (instanceId_ == "") {
00633       thread::Lock lock(globalMutex_);
00634       if (instanceId_ == "") {
00635          Timestamp timestamp = TimestampFactory::getInstance().getTimestamp();
00636          //ContextNode node(this, "instanceId",
00637          //                 lexical_cast<std::string>(timestamp), getContextNode());
00638          instanceId_ = getId() + "/instanceId/" + lexical_cast<std::string>(timestamp);
00639       }
00640    }
00641    return instanceId_;
00642 }
00643 
00644 std::string waitOnKeyboardHit(const std::string &str)
00645 {
00646    char ptr[256];
00647    std::string retStr;
00648 
00649    // Flush input stream
00650    while (true) {
00651       cout.flush();
00652       size_t ret = std::cin.rdbuf()->in_avail();
00653       if (ret == 0) break;
00654       std::cin.getline(ptr,255,'\n');
00655    }
00656 
00657    // Request input
00658    if (str != "")
00659       std::cout << str;
00660 
00661    // Read input, ignore newlines
00662    *ptr = 0;
00663    bool first=true;
00664    while (true) {
00665       std::cin.getline(ptr,255,'\n');
00666       if (strlen(ptr))
00667          retStr = ptr;
00668       else {
00669          if (str != "" && !first)
00670             std::cout << str;
00671       }
00672       first = false;
00673 
00674       size_t ret = std::cin.rdbuf()->in_avail();
00675       if (ret == 0 && retStr != "") {
00676          return retStr;
00677       }
00678    }
00679 }
00680 
00681 }}} // namespace
00682