util/queue/SQLiteQueuePlugin.cpp

Go to the documentation of this file.
00001 /*------------------------------------------------------------------------------
00002 Name:      SQLiteQueuePlugin.cpp
00003 Project:   xmlBlaster.org
00004 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
00005 Author:    xmlBlaster@marcelruff.info
00006 ------------------------------------------------------------------------------*/
00007 #include <util/queue/SQLiteQueuePlugin.h>
00008 #include <util/XmlBlasterException.h>
00009 #include <util/Global.h>
00010 #include <stdarg.h>           // va_start for logging
00011 #include <stdio.h>            // vsnprintf for g++ 2.9x only
00012 #include <util/lexical_cast.h>
00013 #include <util/MessageUnit.h>
00014 #include <util/queue/ConnectQueueEntry.h>
00015 #include <util/queue/SubscribeQueueEntry.h>
00016 #include <util/queue/UnSubscribeQueueEntry.h>
00017 #include <util/queue/PublishQueueEntry.h>
00018 #include <socket/xmlBlasterSocket.h> // C xmlBlaster client library: for msgUnit serialize
00019 #include <util/queue/QueueInterface.h> // The C implementation interface
00020 
00021 using namespace std;
00022 using namespace org::xmlBlaster::util;
00023 using namespace org::xmlBlaster::util::thread;
00024 using namespace org::xmlBlaster::util::qos::storage;
00025 using namespace org::xmlBlaster::util::key;
00026 using namespace org::xmlBlaster::util::qos;
00027 using namespace org::xmlBlaster::client::qos;
00028 using namespace org::xmlBlaster::client::key;
00029 
00030 //static ::XmlBlasterLogging loggingFp = ::xmlBlasterDefaultLogging;
00031 static void myLogger(void *logUserP, 
00032                      XMLBLASTER_LOG_LEVEL currLevel,
00033                      XMLBLASTER_LOG_LEVEL level,
00034                      const char *location, const char *fmt, ...);
00035 
00036 namespace org { namespace xmlBlaster { namespace util { namespace queue {
00037 
00038 SQLiteQueuePlugin::SQLiteQueuePlugin(Global& global, const ClientQueueProperty& property)
00039    : ME("SQLiteQueuePlugin"), 
00040      global_(global), 
00041      log_(global.getLog("org.xmlBlaster.util.queue")), 
00042      property_(property), 
00043      queueP_(0), 
00044      connectQosFactory_(global_),
00045      statusQosFactory_(global_),
00046      msgKeyFactory_(global_),
00047      msgQosFactory_(global_),
00048      accessMutex_()
00049 {
00050    if (log_.call()) log_.call(ME, "Constructor queue [" + getType() + "][" + getVersion() + "] ...");
00051    /*
00052     TODO: Pass basic configuration from plugin key/values similar to (see xmlBlaster.properties)
00053      QueuePlugin[SQLite][1.0]=SQLiteQueuePlugin,
00054          url=/${user.home}${file.separator}tmp${file.separator}$_{xmlBlaster_uniqueId}.db,\
00055          user=sqlite,\
00056          password=,\
00057          connectionPoolSize=1,\
00058          connectionBusyTimeout=90000,\
00059          maxWaitingThreads=300,\
00060          tableNamePrefix=XB_,\
00061          entriesTableName=ENTRIES,\
00062          dbAdmin=true
00063    */
00064    const std::string classRelating = "QueuePlugin["+getType()+"]["+getVersion()+"]"; // "QueuePlugin[SQLite][1.0]"
00065    const std::string instanceRelating = property.getPropertyPrefix();                // == "connection"
00066 
00067    // Should it be "queue/connection/tableNamePrefix" or "queue/QueuePlugin[SQLite][1.0]/tableNamePrefix"
00068    // The first allows different instances with changing "connection" to e.g. "tailback" etc.
00069    if (global_.getProperty().propertyExists(classRelating, true)) {
00070       log_.warn(ME, "Your setting of property '" + classRelating + "' is not supported");
00071    }
00072 
00073    std::string defaultPath = ""; // for example: "/home/joe/tmp/" or "C:\Documents and Settings\marcel\tmp"
00074    if (global_.getProperty().get("user.home", "") != "")
00075       defaultPath = global_.getProperty().get("user.home", "") +
00076                     global_.getProperty().get("file.separator", "");
00077                     //+ "tmp" +                                     // We currently can't create missing directories, TODO!!!
00078                     //global_.getProperty().get("file.separator", "");
00079 
00080    const std::string url = global_.getProperty().get("queue/"+instanceRelating+"/url", defaultPath+"xmlBlasterClientCpp.db");  // "queue/connection/url"
00081    const std::string queueName = global_.getProperty().get("queue/"+instanceRelating+"/queueName", instanceRelating + "_" + global_.getStrippedImmutableId()); // "connection_clientJoe2"
00082    const std::string tableNamePrefix = global_.getProperty().get("queue/"+instanceRelating+"/tableNamePrefix", "XB_");// "queue/connection/tableNamePrefix"
00083 
00084    ::ExceptionStruct exception;
00085    ::QueueProperties queueProperties;
00086    memset(&queueProperties, 0, sizeof(QueueProperties));
00087 
00088    strncpy0(queueProperties.dbName, url.c_str(), QUEUE_DBNAME_MAX);
00089    strncpy0(queueProperties.queueName, queueName.c_str(), QUEUE_ID_MAX);
00090    strncpy0(queueProperties.tablePrefix, tableNamePrefix.c_str(), QUEUE_PREFIX_MAX);
00091    queueProperties.maxNumOfEntries = (int32_t)property.getMaxEntries();
00092    queueProperties.maxNumOfBytes = property.getMaxBytes();
00093    queueProperties.logFp = myLogger;
00094    queueProperties.logLevel = (log_.call() || log_.trace()) ? XMLBLASTER_LOG_TRACE : XMLBLASTER_LOG_INFO;
00095    queueProperties.userObject = &log_;
00096 
00097    queueP_ = createQueue(&queueProperties, &exception); // &log_ Used in myLogger(), see above
00098    if (*exception.errorCode != 0) throw convertFromQueueException(&exception);
00099 
00100    log_.info(ME, "Created queue [" + getType() + "][" + getVersion() + "], queue/"+instanceRelating+"/url='" +
00101                  queueProperties.dbName + "', queue/"+instanceRelating+"/queueName='" + queueProperties.queueName +
00102                  "', queue/"+instanceRelating+"/maxEntries=" + lexical_cast<string>(queueProperties.maxNumOfEntries));
00103 }
00104 
00105 /*
00106 SQLiteQueuePlugin::SQLiteQueuePlugin(const SQLiteQueuePlugin& queue)
00107    : ME("SQLiteQueuePlugin"), 
00108      global_(queue.global_), 
00109      log_(queue.log_), 
00110      property_(queue.property_), 
00111      queueP_(queue.queueP_), 
00112      accessMutex_()
00113 {
00114 }
00115 
00116 SQLiteQueuePlugin& SQLiteQueuePlugin::operator =(const SQLiteQueuePlugin& queue)
00117 {
00118    Lock lock(queue.accessMutex_);
00119    property_   = queue.property_;
00120    queueP_    = queue.queueP_;
00121    return *this;
00122 
00123 }
00124 */
00125 
00126 SQLiteQueuePlugin::~SQLiteQueuePlugin()
00127 {
00128    if (log_.call()) log_.call(ME, "destructor");
00129    if (queueP_) {
00130       Lock lock(accessMutex_);
00131       ::ExceptionStruct exception;
00132       queueP_->shutdown(&queueP_, &exception); // NULLs the queueP_
00133       if (*exception.errorCode != 0) {
00134          const int ERRORSTR_LEN = 1024;
00135          char errorString[ERRORSTR_LEN];
00136          log_.warn(ME, string("Ignoring problem during shutdown: ") + getExceptionStr(errorString, ERRORSTR_LEN, &exception));
00137       }
00138    }
00139 } 
00140 
00141 void SQLiteQueuePlugin::put(const MsgQueueEntry &entry)
00142 {
00143    if (log_.call()) log_.call(ME, "::put");
00144    if (log_.dump()) log_.dump(ME+".put()", string("The msg entry is: ")  + entry.toXml());
00145 
00146    Lock lock(accessMutex_);
00147    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, put() failed");
00148 
00149    ::ExceptionStruct exception;
00150    ::QueueEntry queueEntry;
00151 
00152    // Copy C++ to C struct ...
00153 
00154    queueEntry.priority = entry.getPriority();
00155    queueEntry.isPersistent = entry.isPersistent();
00156    queueEntry.uniqueId = entry.getUniqueId();
00157    queueEntry.sizeInBytes = entry.getSizeInBytes();
00158    strncpy0(queueEntry.embeddedType, entry.getEmbeddedType().c_str(), QUEUE_ENTRY_EMBEDDEDTYPE_LEN);  // "MSG_RAW|publish"
00159    queueEntry.embeddedType[QUEUE_ENTRY_EMBEDDEDTYPE_LEN-1] = 0;
00160 
00161    // dump MsgQueueEntry with SOCKET protocol into C ::MsgUnit ...
00162    
00163    const BlobHolder *blob = (const BlobHolder *)entry.getEmbeddedObject();
00164    if (blob == 0) throw XmlBlasterException(INTERNAL_ILLEGALARGUMENT, ME, "put() failed, the entry " + entry.getLogId() + " returned NULL for embeddedObject");
00165    queueEntry.embeddedBlob.data = blob->data;
00166    queueEntry.embeddedBlob.dataLen = blob->dataLen;
00167 
00168    if (log_.dump()) {
00169       char *dumpP = blobDump(&queueEntry.embeddedBlob);
00170       log_.dump(ME+".put()", string("Put blob to queue:") + dumpP);
00171       ::xmlBlasterFree(dumpP);
00172    }
00173 
00174    // Push into C persistent queue ...
00175 
00176    queueP_->put(queueP_, &queueEntry, &exception);
00177 
00178    if (*exception.errorCode != 0) throw convertFromQueueException(&exception);
00179 }
00180 
00181 const vector<EntryType> SQLiteQueuePlugin::peekWithSamePriority(long maxNumOfEntries, long maxNumOfBytes) const
00182 {
00183    Lock lock(accessMutex_);
00184    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, peekWithSamePriority() failed");
00185    vector<EntryType> ret;
00186    if (queueP_->empty(queueP_)) return ret;
00187    if (log_.call()) log_.call(ME, "peekWithSamePriority maxNumOfEntries=" + lexical_cast<std::string>(maxNumOfEntries) + " maxNumOfBytes=" + lexical_cast<std::string>(maxNumOfBytes));
00188 
00189    ::ExceptionStruct exception;
00190    ::QueueEntryArr *entriesC = queueP_->peekWithSamePriority(queueP_, (int32_t)maxNumOfEntries, maxNumOfBytes, &exception);
00191    if (*exception.errorCode != 0) throw convertFromQueueException(&exception);
00192 
00193    // Now we need to copy the C results into C++ classes ...
00194 
00195    for (size_t ii=0; ii<entriesC->len; ii++) {
00196       ::QueueEntry &queueEntryC = entriesC->queueEntryArr[ii];
00197       string type, methodName;
00198       parseEmbeddedType(queueEntryC.embeddedType, type, methodName);
00199 
00200       if (type != Constants::ENTRY_TYPE_MSG_RAW) {
00201          string embedded = queueEntryC.embeddedType;
00202          freeQueueEntryArr(entriesC);
00203          throw XmlBlasterException(INTERNAL_UNKNOWN, ME + "::peekWithSamePriority", string("The queue entry embeddedType '") + embedded + "' type='" + type + "' is not supported");
00204       }
00205 
00206       if (log_.dump()) {
00207          char *dumpP = blobDump(&queueEntryC.embeddedBlob);
00208          log_.dump(ME+".peekWithSamePriority()", string("Retrieved blob from queue:") + dumpP);
00209          ::xmlBlasterFree(dumpP);
00210       }
00211 
00212       ::MsgUnitArr *msgUnitArrC = ::parseMsgUnitArr(queueEntryC.embeddedBlob.dataLen, queueEntryC.embeddedBlob.data);
00213 
00214       for (size_t j=0; msgUnitArrC!=0 && j<msgUnitArrC->len; j++) { // TODO: Collect a PUBLISH_ARR !!! (currently we transform it to single publish()es)
00215          ::MsgUnit &msgUnit = msgUnitArrC->msgUnitArr[j];
00216          if (log_.dump()) {
00217             char *dumpP = ::messageUnitToXmlLimited(&msgUnit, 128);
00218             log_.dump(ME+".peekWithSamePriority()", string("Retrieved and parsed C message from queue:") + dumpP);
00219             ::xmlBlasterFree(dumpP);
00220          }
00221          if (methodName == MethodName::PUBLISH) {
00222             MsgKeyData msgKeyData = msgKeyFactory_.readObject(string(msgUnit.key));
00223             MsgQosData msgQosData = msgQosFactory_.readObject(string(msgUnit.qos));
00224             MessageUnit messageUnit(msgKeyData, msgUnit.contentLen, (const unsigned char*)msgUnit.content, msgQosData);
00225             PublishQueueEntry *pq = new PublishQueueEntry(global_, messageUnit,
00226                                            queueEntryC.priority, queueEntryC.uniqueId);
00227             if (log_.trace()) log_.trace(ME, "Got PublishQueueEntry from queue");
00228             ret.insert(ret.end(), EntryType(*pq));
00229             if (log_.trace()) log_.trace(ME, "PublishQueueEntry is reference countet");
00230          }
00231          else if (methodName == MethodName::CONNECT) {
00232             ConnectQosRef connectQos = connectQosFactory_.readObject(string(msgUnit.qos));
00233             ConnectQueueEntry *pq = new ConnectQueueEntry(global_, connectQos,
00234                                            queueEntryC.priority, queueEntryC.uniqueId);
00235             if (log_.trace()) log_.trace(ME, "Got ConnectQueueEntry from queue");
00236             ret.insert(ret.end(), EntryType(*pq));
00237             if (log_.trace()) log_.trace(ME, "ConnectQueueEntry is reference countet");
00238          }
00239          /* TODO: queryKeyFactory and queryQosFactory!
00240          else if (methodName == MethodName::SUBSCRIBE) {
00241             QueryKeyData queryKeyData = queryKeyFactory_.readObject(string(msgUnit.key));
00242             SubscribeKey subscribeKey(global_, queryKeyData);
00243             QueryQosData queryQosData = queryQosFactory_.readObject(string(msgUnit.qos));
00244             SubscribeQos subscribeQos(global_, queryQosData);
00245             SubscribeQueueEntry *pq = new SubscribeQueueEntry(global_, subscribeKey, subscribeQos,
00246                                            queueEntryC.priority, queueEntryC.uniqueId);
00247             if (log_.trace()) log_.trace(ME, "Got SubscribeQueueEntry from queue");
00248             ret.insert(ret.end(), EntryType(*pq));
00249             if (log_.trace()) log_.trace(ME, "SubscribeQueueEntry is reference countet");
00250          }
00251          */
00252          else {  // TODO: How to handle: throw exception or remove the buggy entry?
00253             log_.error(ME + "::peekWithSamePriority", string("The queue entry embeddedType '") + queueEntryC.embeddedType + "' methodName='" + methodName + "' is not supported, we ignore it.");
00254          }
00255       }
00256 
00257       freeMsgUnitArr(msgUnitArrC);
00258    }
00259 
00260    freeQueueEntryArr(entriesC);
00261    return ret;
00262 }
00263 
00264 void SQLiteQueuePlugin::parseEmbeddedType(const string& embeddedType, string &type, string &methodName)
00265 {
00266    string::size_type pos = embeddedType.find("|");
00267    if (pos == string::npos) {
00268       type = embeddedType;
00269       methodName = "";
00270       return;
00271    }
00272    type = embeddedType.substr(0, pos);
00273    if (pos < embeddedType.size())
00274       methodName = embeddedType.substr(pos+1);
00275    // No trim(): we assume no white spaces
00276 }
00277 
00278 long SQLiteQueuePlugin::randomRemove(const vector<EntryType>::const_iterator &start, const vector<EntryType>::const_iterator &end) 
00279 {
00280    Lock lock(accessMutex_);
00281    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, randomRemove() failed");
00282    if (start == end || queueP_->empty(queueP_)) return 0;
00283 
00284    ::QueueEntryArr queueEntryArr;
00285    memset(&queueEntryArr, 0, sizeof(QueueEntryArr));
00286    {
00287       vector<EntryType>::const_iterator iter = start;
00288       while (iter != end) {
00289          iter++;
00290          queueEntryArr.len++;
00291       }
00292    }
00293    if (queueEntryArr.len < 1) return 0;
00294 
00295    queueEntryArr.queueEntryArr = (QueueEntry *)calloc(queueEntryArr.len, sizeof(QueueEntry));
00296 
00297    vector<EntryType>::const_iterator iter = start;
00298    for (int currIndex=0; iter != end; ++iter, currIndex++) {
00299       const EntryType &entryType = (*iter);
00300       const MsgQueueEntry &entry = *entryType;
00301       ::QueueEntry &queueEntry = queueEntryArr.queueEntryArr[currIndex];
00302 
00303       // Copy C++ to C struct ...
00304 
00305       queueEntry.priority = entry.getPriority();
00306       queueEntry.isPersistent = entry.isPersistent();
00307       queueEntry.uniqueId = entry.getUniqueId();
00308       queueEntry.sizeInBytes = entry.getSizeInBytes();
00309       strncpy0(queueEntry.embeddedType, entry.getEmbeddedType().c_str(), QUEUE_ENTRY_EMBEDDEDTYPE_LEN);  // "MSG_RAW|publish" "MSG_RAW|connect"
00310       queueEntry.embeddedType[QUEUE_ENTRY_EMBEDDEDTYPE_LEN-1] = 0;
00311       /*
00312       const BlobHolder *blob = (const BlobHolder *)entry.getEmbeddedObject();
00313       if (blob == 0) throw XmlBlasterException(INTERNAL_ILLEGALARGUMENT, ME, "put() failed, the entry " + entry.getLogId() + " returned NULL for embeddedObject");
00314       queueEntry.embeddedBlob.data = blob->data;
00315       queueEntry.embeddedBlob.dataLen = blob->dataLen;
00316       */
00317       if (log_.dump()) {
00318          char *dumpP = ::queueEntryToXml(&queueEntry, 200);
00319          log_.dump(ME+".put()", string("Put blob to queue:") + dumpP);
00320          xmlBlasterFree(dumpP);
00321       }
00322    }
00323 
00324    ::ExceptionStruct exception;
00325 
00326    int32_t numRemoved = queueP_->randomRemove(queueP_, &queueEntryArr, &exception);
00327 
00328    freeQueueEntryArrInternal(&queueEntryArr);
00329 
00330    if (*exception.errorCode != 0) throw convertFromQueueException(&exception);
00331    return (long)numRemoved;
00332 }
00333 
00334 long SQLiteQueuePlugin::getNumOfEntries() const
00335 {
00336    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, getNumOfEntries() failed");
00337    return queueP_->getNumOfEntries(queueP_);
00338 }
00339 
00340 long SQLiteQueuePlugin::getMaxNumOfEntries() const
00341 {
00342    if (queueP_ == 0) return property_.getMaxEntries(); // throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, getNumOfEntries() failed");
00343    return queueP_->getMaxNumOfEntries(queueP_);
00344 }
00345 
00346 int64_t SQLiteQueuePlugin::getNumOfBytes() const
00347 {
00348    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, getNumOfBytes() failed");
00349    return queueP_->getNumOfBytes(queueP_);
00350 }
00351 
00352 int64_t SQLiteQueuePlugin::getMaxNumOfBytes() const
00353 {  
00354    if (queueP_ == 0) return property_.getMaxBytes(); // throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, getMaxNumOfBytes() failed");
00355    return queueP_->getMaxNumOfBytes(queueP_);
00356 }
00357 
00358 void SQLiteQueuePlugin::clear()
00359 {
00360    Lock lock(accessMutex_);
00361    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, clear() failed");
00362    ::ExceptionStruct exception;
00363    queueP_->clear(queueP_, &exception);
00364 }
00365 
00366 
00367 bool SQLiteQueuePlugin::empty() const
00368 {
00369    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, empty() failed");
00370    return queueP_->empty(queueP_);
00371 }
00372 
00373 void SQLiteQueuePlugin::destroy()
00374 {
00375    if (queueP_ == 0) throw XmlBlasterException(RESOURCE_DB_UNAVAILABLE, ME, "Sorry, no persistent queue is available, destroy() failed");
00376    ::ExceptionStruct exception;
00377    queueP_->destroy(&queueP_, &exception);
00378    if (*exception.errorCode != 0) throw convertFromQueueException(&exception);
00379 }
00380 
00381 // Exception conversion ....
00382 org::xmlBlaster::util::XmlBlasterException SQLiteQueuePlugin::convertFromQueueException(const ::ExceptionStruct *ex) const
00383 {
00384    return org::xmlBlaster::util::XmlBlasterException(
00385             (*ex->errorCode=='\0')?string("internal.unknown"):string(ex->errorCode),
00386             string(""),
00387             ME,
00388             "en",
00389             string(ex->message),
00390             global_.getVersion() + " " + global_.getBuildTimestamp());
00391 }
00392 
00393 string SQLiteQueuePlugin::usage()
00394 {
00395    std::string text = string("");
00396    text += string("\nThe SQLite persistent queue plugin configuration:");
00397    text += string("\n   -queue/connection/url [xmlBlasterClientCpp.db]");
00398    text += string("\n                       The database file name (incl. path), defaults to the current directory.");
00399    text += string("\n   -queue/connection/tableNamePrefix [XB_]");
00400    text += string("\n                       The prefix for all tables in the database.");
00401    text += ClientQueueProperty::usage();
00402    return text;
00403 }
00404 
00405 }}}} // namespace
00406 
00407 
00423 static void myLogger(void *logUserP, 
00424                      XMLBLASTER_LOG_LEVEL currLevel,
00425                      XMLBLASTER_LOG_LEVEL level,
00426                      const char *location, const char *fmt, ...)
00427 {
00428    /* Guess we need no more than 200 bytes. */
00429    int n, size = 200;
00430    char *p = 0;
00431    va_list ap;
00432    ::I_Queue *queueP = (::I_Queue *)logUserP;
00433 
00434    //org::xmlBlaster::util::queue::SQLiteQueuePlugin *pluginP =
00435    //      (org::xmlBlaster::util::queue::SQLiteQueuePlugin *)queueP->userObject;
00436    //org::xmlBlaster::util::I_Log& log = pluginP->getLog();
00437 
00438    if (queueP->userObject == 0) {
00439       std::cout << "myLogger not initialized" << std::endl;
00440       return;
00441    }
00442    org::xmlBlaster::util::I_Log& log = *((org::xmlBlaster::util::I_Log*)queueP->userObject);
00443 
00444    if (level > currLevel) { /* XMLBLASTER_LOG_ERROR, XMLBLASTER_LOG_WARN, XMLBLASTER_LOG_INFO, XMLBLASTER_LOG_TRACE */
00445       return;
00446    }
00447    if ((p = (char *)malloc (size)) == NULL)
00448       return;
00449 
00450    for (;;) {
00451       /* Try to print in the allocated space. */
00452       va_start(ap, fmt);
00453       n = VSNPRINTF(p, size, fmt, ap); /* UNIX: vsnprintf(), WINDOWS: _vsnprintf() */
00454       va_end(ap);
00455       /* If that worked, print the string to console. */
00456       if (n > -1 && n < size) {
00457          if (level == XMLBLASTER_LOG_INFO)
00458             log.info(location, p);
00459          else if (level == XMLBLASTER_LOG_WARN)
00460             log.warn(location, p);
00461          else if (level == XMLBLASTER_LOG_ERROR)
00462             log.error(location, p);
00463          else
00464             log.trace(location, p);
00465          free(p);
00466          return;
00467       }
00468       /* Else try again with more space. */
00469       if (n > -1)    /* glibc 2.1 */
00470          size = n+1; /* precisely what is needed */
00471       else           /* glibc 2.0 */
00472          size *= 2;  /* twice the old size */
00473       if ((p = (char *)realloc (p, size)) == NULL) {
00474          return;
00475       }
00476    }
00477 }
00478