IBR-DTNSuite 0.6

ibrcommon/ibrcommon/data/BLOB.cpp

Go to the documentation of this file.
00001 /*
00002  * BLOB.cpp
00003  *
00004  *  Created on: 15.12.2009
00005  *      Author: morgenro
00006  */
00007 
00008 #include "ibrcommon/data/BLOB.h"
00009 #include "ibrcommon/thread/MutexLock.h"
00010 #include "ibrcommon/Exceptions.h"
00011 #include "ibrcommon/Logger.h"
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <cstring>
00016 #include <cerrno>
00017 
00018 #ifdef __DEVELOPMENT_ASSERTIONS__
00019 #include <cassert>
00020 #endif
00021 
00022 namespace ibrcommon
00023 {
00024         // maximum of concurrent opened files
00025         ibrcommon::Semaphore BLOB::_filelimit(10);
00026 
00027         // default BLOB provider - memory based; auto deletion enabled
00028         ibrcommon::BLOB::ProviderRef BLOB::provider(new ibrcommon::MemoryBLOBProvider(), true);
00029 
00030         BLOB::BLOB()
00031          : ibrcommon::Mutex(ibrcommon::Mutex::MUTEX_NORMAL), _locked(false), _refcount(0), _reflock(ibrcommon::Mutex::MUTEX_NORMAL)
00032         { }
00033 
00034         BLOB::~BLOB()
00035         {
00036         }
00037 
00038         std::ostream& BLOB::copy(std::ostream &output, std::istream &input, const size_t size, const size_t buffer_size)
00039         {
00040                 // read payload
00041                 char buffer[buffer_size];
00042                 size_t remain = size;
00043 
00044                 while (remain > 0)
00045                 {
00046                         // something bad happened, abort!
00047                         if (input.bad())
00048                         {
00049                                 std::stringstream errmsg; errmsg << "input stream went bad [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied";
00050                                 throw ibrcommon::IOException(errmsg.str());
00051                         }
00052 
00053                         // reached EOF too early
00054                         if (input.eof())
00055                         {
00056                                 std::stringstream errmsg; errmsg << "input stream reached EOF [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied";
00057                                 throw ibrcommon::IOException(errmsg.str());
00058                         }
00059 
00060                         // check if the destination stream is ok
00061                         if (output.bad())
00062                         {
00063                                 std::stringstream errmsg; errmsg << "output stream went bad [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied";
00064                                 throw ibrcommon::IOException(errmsg.str());
00065                         }
00066 
00067                         // retry if the read failed
00068                         if (input.fail())
00069                         {
00070                                 IBRCOMMON_LOGGER(warning) << "input stream failed [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied" << IBRCOMMON_LOGGER_ENDL;
00071                                 input.clear();
00072                         }
00073 
00074                         // if the last write failed, then retry
00075                         if (output.fail())
00076                         {
00077                                 IBRCOMMON_LOGGER(warning) << "output stream failed [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied" << IBRCOMMON_LOGGER_ENDL;
00078                                 output.clear();
00079                         }
00080                         else
00081                         {
00082                                 // read the full buffer size of less?
00083                                 if (remain > buffer_size)
00084                                 {
00085                                         input.read(buffer, buffer_size);
00086                                 }
00087                                 else
00088                                 {
00089                                         input.read(buffer, remain);
00090                                 }
00091 
00092                                 // retry if the read failed
00093                                 if (input.fail()) continue;
00094                         }
00095 
00096                         // write the bytes to the BLOB
00097                         output.write(buffer, input.gcount());
00098 
00099                         // shrink the remaining bytes by the red bytes
00100                         remain -= input.gcount();
00101                 }
00102 
00103                 return output;
00104         }
00105 
00106         ibrcommon::BLOB::Reference BLOB::create()
00107         {
00108                 return ibrcommon::BLOB::provider.create();
00109         }
00110 
00111         ibrcommon::BLOB::Reference BLOB::open(const ibrcommon::File &f)
00112         {
00113                 return ibrcommon::BLOB::Reference(new ibrcommon::FileBLOB(f));
00114         }
00115 
00116         void BLOB::changeProvider(BLOB::Provider *p, bool auto_delete)
00117         {
00118                 ibrcommon::BLOB::provider.change(p, auto_delete);
00119         }
00120 
00121         BLOB::Provider::~Provider()
00122         { };
00123 
00124         BLOB::ProviderRef::ProviderRef(Provider *provider, bool auto_delete)
00125          : _provider(provider), _auto_delete(auto_delete)
00126         {
00127         }
00128 
00129         BLOB::ProviderRef::~ProviderRef()
00130         {
00131                 if (_auto_delete)
00132                 {
00133                         delete _provider;
00134                 }
00135         }
00136 
00137         void BLOB::ProviderRef::change(BLOB::Provider *p, bool auto_delete)
00138         {
00139                 if (_auto_delete)
00140                 {
00141                         delete _provider;
00142                 }
00143 
00144                 _provider = p;
00145                 _auto_delete = auto_delete;
00146         }
00147 
00148         BLOB::Reference BLOB::ProviderRef::create()
00149         {
00150                 return _provider->create();
00151         }
00152 
00153         MemoryBLOBProvider::MemoryBLOBProvider()
00154         {
00155         }
00156 
00157         MemoryBLOBProvider::~MemoryBLOBProvider()
00158         {
00159         }
00160 
00161         ibrcommon::BLOB::Reference MemoryBLOBProvider::create()
00162         {
00163                 return StringBLOB::create();
00164         }
00165 
00166         FileBLOBProvider::FileBLOBProvider(const File &path)
00167          : _tmppath(path)
00168         {
00169         }
00170 
00171         FileBLOBProvider::~FileBLOBProvider()
00172         {
00173         }
00174 
00175         ibrcommon::BLOB::Reference FileBLOBProvider::create()
00176         {
00177                 return ibrcommon::BLOB::Reference(new TmpFileBLOB(_tmppath));
00178         }
00179 
00180         BLOB::Reference::Reference(const Reference &ref)
00181          : ibrcommon::Mutex(MUTEX_NORMAL), _blob(ref._blob)
00182         {
00183                 MutexLock l(_blob->_reflock);
00184                 _blob->_refcount++;
00185         }
00186 
00187         BLOB::Reference::~Reference()
00188         {
00189                 // check if this is the last reference
00190                 {
00191                         MutexLock l(_blob->_reflock);
00192                         if ((_blob->_refcount--) > 1) return;
00193                 }
00194 
00195                 delete _blob;
00196         }
00197 
00198         BLOB::iostream BLOB::Reference::iostream()
00199         {
00200                 return BLOB::iostream(_blob);
00201         }
00202 
00203         std::iostream& BLOB::Reference::operator*()
00204         {
00205 #ifdef __DEVELOPMENT_ASSERTIONS__
00206                 assert(_blob->_locked);
00207 #endif
00208                 return _blob->__get_stream();
00209         }
00210 
00211         BLOB::Reference::Reference(BLOB *blob)
00212          : _blob(blob)
00213         {
00214                 MutexLock l(_blob->_reflock);
00215                 _blob->_refcount++;
00216         }
00217 
00218         void BLOB::Reference::enter() throw (ibrcommon::MutexException)
00219         {
00220                 // lock the stream
00221                 _blob->enter();
00222                 _blob->_locked = true;
00223 
00224                 // open the stream
00225                 _blob->open();
00226         }
00227 
00228         void BLOB::Reference::leave() throw (ibrcommon::MutexException)
00229         {
00230                 _blob->_locked = false;
00231 
00232                 // close the stream
00233                 _blob->close();
00234 
00235                 // unlock the stream
00236                 _blob->leave();
00237         }
00238 
00239         void BLOB::Reference::trylock() throw (ibrcommon::MutexException)
00240         {
00241                 // lock the stream
00242                 _blob->trylock();
00243                 _blob->_locked = true;
00244 
00245                 // open the stream
00246                 _blob->open();
00247         }
00248 
00249         size_t BLOB::Reference::getSize() const
00250         {
00251 #ifdef __DEVELOPMENT_ASSERTIONS__
00252                 assert(_blob->_locked);
00253 #endif
00254                 return _blob->__get_size();
00255         }
00256 
00257         void BLOB::Reference::clear()
00258         {
00259 #ifdef __DEVELOPMENT_ASSERTIONS__
00260                 assert(_blob->_locked);
00261 #endif
00262                 _blob->clear();
00263         }
00264 
00265         const BLOB* BLOB::Reference::getPointer() const
00266         {
00267                 return _blob;
00268         }
00269 
00270         BLOB::Reference MemoryBLOBProvider::StringBLOB::create()
00271         {
00272                 BLOB::Reference ref(new MemoryBLOBProvider::StringBLOB());
00273                 return ref;
00274         }
00275 
00276         void MemoryBLOBProvider::StringBLOB::clear()
00277         {
00278                 _stringstream.str("");
00279         }
00280 
00281         MemoryBLOBProvider::StringBLOB::StringBLOB()
00282          : BLOB(), _stringstream()
00283         {
00284 
00285         }
00286 
00287         MemoryBLOBProvider::StringBLOB::~StringBLOB()
00288         {
00289         }
00290 
00291         void MemoryBLOBProvider::StringBLOB::open()
00292         {
00293                 // set pointer to the beginning of the stream and remove any error flags
00294                 _stringstream.clear();
00295                 _stringstream.seekp(0);
00296                 _stringstream.seekg(0);
00297         }
00298 
00299         void MemoryBLOBProvider::StringBLOB::close()
00300         {
00301         }
00302 
00303         size_t MemoryBLOBProvider::StringBLOB::__get_size()
00304         {
00305                 // store current position
00306                 size_t pos = _stringstream.tellg();
00307 
00308                 _stringstream.seekg(0, std::ios_base::end);
00309                 size_t size = _stringstream.tellg();
00310                 _stringstream.seekg(pos);
00311 
00312                 return size;
00313         }
00314 
00315         void FileBLOB::clear()
00316         {
00317                 throw ibrcommon::IOException("clear is not possible on a read only file");
00318         }
00319 
00320         FileBLOB::FileBLOB(const File &f)
00321          : ibrcommon::BLOB(), _filestream(), _file(f)
00322         {
00323                 if (!f.exists())
00324                 {
00325                         throw ibrcommon::FileNotExistsException(f);
00326                 }
00327         }
00328 
00329         FileBLOB::~FileBLOB()
00330         {
00331         }
00332 
00333         void FileBLOB::open()
00334         {
00335                 BLOB::_filelimit.wait();
00336 
00337                 // open the file
00338                 _filestream.open(_file.getPath().c_str(), ios::in|ios::binary);
00339 
00340                 if (!_filestream.is_open())
00341                 {
00342                         throw ibrcommon::CanNotOpenFileException(_file);
00343                 }
00344         }
00345 
00346         void FileBLOB::close()
00347         {
00348                 // close the file
00349                 _filestream.close();
00350 
00351                 BLOB::_filelimit.post();
00352         }
00353 
00354         size_t FileBLOB::__get_size()
00355         {
00356                 return _file.size();
00357         }
00358 
00359         void FileBLOBProvider::TmpFileBLOB::clear()
00360         {
00361                 // close the file
00362                 _filestream.close();
00363 
00364                 // open temporary file
00365                 _filestream.open(_tmpfile.getPath().c_str(), ios::in | ios::out | ios::trunc | ios::binary );
00366 
00367                 if (!_filestream.is_open())
00368                 {
00369                         IBRCOMMON_LOGGER(error) << "can not open temporary file " << _tmpfile.getPath() << IBRCOMMON_LOGGER_ENDL;
00370                         throw ibrcommon::CanNotOpenFileException(_tmpfile);
00371                 }
00372         }
00373 
00374         FileBLOBProvider::TmpFileBLOB::TmpFileBLOB(const File &tmppath)
00375          : BLOB(), _filestream()
00376         {
00377                 // check if path is a directory
00378                 if (!tmppath.isDirectory())
00379                 {
00380                         throw ibrcommon::IOException("BLOB::tmppath not initialized or path is not a directory.");
00381                 }
00382 
00383                 _tmpfile = ibrcommon::TemporaryFile(tmppath, "blob");
00384                 _tmpfile.update();
00385         }
00386 
00387         FileBLOBProvider::TmpFileBLOB::~TmpFileBLOB()
00388         {
00389                 // delete the file if the last reference is destroyed
00390                 _tmpfile.remove();
00391         }
00392 
00393         void FileBLOBProvider::TmpFileBLOB::open()
00394         {
00395                 BLOB::_filelimit.wait();
00396 
00397                 // open temporary file
00398                 _filestream.open(_tmpfile.getPath().c_str(), ios::in | ios::out | ios::binary );
00399 
00400                 if (!_filestream.is_open())
00401                 {
00402                         IBRCOMMON_LOGGER(error) << "can not open temporary file " << _tmpfile.getPath() << IBRCOMMON_LOGGER_ENDL;
00403                         throw ibrcommon::CanNotOpenFileException(_tmpfile);
00404                 }
00405         }
00406 
00407         void FileBLOBProvider::TmpFileBLOB::close()
00408         {
00409                 // flush the filestream
00410                 _filestream.flush();
00411 
00412                 // close the file
00413                 _filestream.close();
00414 
00415                 BLOB::_filelimit.post();
00416         }
00417 
00418         size_t FileBLOBProvider::TmpFileBLOB::__get_size()
00419         {
00420                 return _tmpfile.size();
00421         }
00422 }