|
IBR-DTNSuite 0.6
|
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 }