|
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 ibrcommon::Semaphore BLOB::_filelimit(10); 00025 00026 File BLOB::tmppath; 00027 00028 BLOB::BLOB() 00029 : ibrcommon::Mutex(ibrcommon::Mutex::MUTEX_NORMAL), _locked(false), _refcount(0), _reflock(ibrcommon::Mutex::MUTEX_NORMAL) 00030 { } 00031 00032 BLOB::~BLOB() 00033 { 00034 } 00035 00036 std::ostream& BLOB::copy(std::ostream &output, std::istream &input, const size_t size, const size_t buffer_size) 00037 { 00038 // read payload 00039 char buffer[buffer_size]; 00040 size_t remain = size; 00041 00042 while (remain > 0) 00043 { 00044 // something bad happened, abort! 00045 if (input.bad()) 00046 { 00047 std::stringstream errmsg; errmsg << "input stream went bad [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied"; 00048 throw ibrcommon::IOException(errmsg.str()); 00049 } 00050 00051 // reached EOF too early 00052 if (input.eof()) 00053 { 00054 std::stringstream errmsg; errmsg << "input stream reached EOF [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied"; 00055 throw ibrcommon::IOException(errmsg.str()); 00056 } 00057 00058 // check if the destination stream is ok 00059 if (output.bad()) 00060 { 00061 std::stringstream errmsg; errmsg << "output stream went bad [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied"; 00062 throw ibrcommon::IOException(errmsg.str()); 00063 } 00064 00065 // retry if the read failed 00066 if (input.fail()) 00067 { 00068 IBRCOMMON_LOGGER(warning) << "input stream failed [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied" << IBRCOMMON_LOGGER_ENDL; 00069 input.clear(); 00070 } 00071 00072 // if the last write failed, then retry 00073 if (output.fail()) 00074 { 00075 IBRCOMMON_LOGGER(warning) << "output stream failed [" << std::strerror(errno) << "]; " << (size-remain) << " of " << size << " bytes copied" << IBRCOMMON_LOGGER_ENDL; 00076 output.clear(); 00077 } 00078 else 00079 { 00080 // read the full buffer size of less? 00081 if (remain > buffer_size) 00082 { 00083 input.read(buffer, buffer_size); 00084 } 00085 else 00086 { 00087 input.read(buffer, remain); 00088 } 00089 00090 // retry if the read failed 00091 if (input.fail()) continue; 00092 } 00093 00094 // write the bytes to the BLOB 00095 output.write(buffer, input.gcount()); 00096 00097 // shrink the remaining bytes by the red bytes 00098 remain -= input.gcount(); 00099 } 00100 00101 return output; 00102 } 00103 00104 BLOB::Reference::Reference(const Reference &ref) 00105 : ibrcommon::Mutex(MUTEX_NORMAL), _blob(ref._blob) 00106 { 00107 MutexLock l(_blob->_reflock); 00108 _blob->_refcount++; 00109 } 00110 00111 BLOB::Reference::~Reference() 00112 { 00113 // check if this is the last reference 00114 { 00115 MutexLock l(_blob->_reflock); 00116 if ((_blob->_refcount--) > 1) return; 00117 } 00118 00119 delete _blob; 00120 } 00121 00122 BLOB::iostream BLOB::Reference::iostream() 00123 { 00124 return BLOB::iostream(_blob); 00125 } 00126 00127 std::iostream& BLOB::Reference::operator*() 00128 { 00129 #ifdef __DEVELOPMENT_ASSERTIONS__ 00130 assert(_blob->_locked); 00131 #endif 00132 return _blob->__get_stream(); 00133 } 00134 00135 BLOB::Reference::Reference(BLOB *blob) 00136 : _blob(blob) 00137 { 00138 MutexLock l(_blob->_reflock); 00139 _blob->_refcount++; 00140 } 00141 00142 void BLOB::Reference::enter() throw (ibrcommon::MutexException) 00143 { 00144 // lock the stream 00145 _blob->enter(); 00146 _blob->_locked = true; 00147 00148 // open the stream 00149 _blob->open(); 00150 } 00151 00152 void BLOB::Reference::leave() throw (ibrcommon::MutexException) 00153 { 00154 _blob->_locked = false; 00155 00156 // close the stream 00157 _blob->close(); 00158 00159 // unlock the stream 00160 _blob->leave(); 00161 } 00162 00163 void BLOB::Reference::trylock() throw (ibrcommon::MutexException) 00164 { 00165 // lock the stream 00166 _blob->trylock(); 00167 _blob->_locked = true; 00168 00169 // open the stream 00170 _blob->open(); 00171 } 00172 00173 size_t BLOB::Reference::getSize() const 00174 { 00175 #ifdef __DEVELOPMENT_ASSERTIONS__ 00176 assert(_blob->_locked); 00177 #endif 00178 return _blob->__get_size(); 00179 } 00180 00181 void BLOB::Reference::clear() 00182 { 00183 #ifdef __DEVELOPMENT_ASSERTIONS__ 00184 assert(_blob->_locked); 00185 #endif 00186 _blob->clear(); 00187 } 00188 00189 BLOB::Reference StringBLOB::create() 00190 { 00191 BLOB::Reference ref(new StringBLOB()); 00192 return ref; 00193 } 00194 00195 void StringBLOB::clear() 00196 { 00197 _stringstream.str(""); 00198 } 00199 00200 StringBLOB::StringBLOB() 00201 : BLOB(), _stringstream() 00202 { 00203 00204 } 00205 00206 StringBLOB::~StringBLOB() 00207 { 00208 } 00209 00210 void StringBLOB::open() 00211 { 00212 // set pointer to the beginning of the stream and remove any error flags 00213 _stringstream.clear(); 00214 _stringstream.seekp(0); 00215 _stringstream.seekg(0); 00216 } 00217 00218 void StringBLOB::close() 00219 { 00220 } 00221 00222 size_t StringBLOB::__get_size() 00223 { 00224 // store current position 00225 size_t pos = _stringstream.tellg(); 00226 00227 _stringstream.seekg(0, std::ios_base::end); 00228 size_t size = _stringstream.tellg(); 00229 _stringstream.seekg(pos); 00230 00231 return size; 00232 } 00233 00234 BLOB::Reference FileBLOB::create(const File &f) 00235 { 00236 return BLOB::Reference(new FileBLOB(f)); 00237 } 00238 00239 void FileBLOB::clear() 00240 { 00241 throw ibrcommon::IOException("clear is not possible on a read only file"); 00242 } 00243 00244 FileBLOB::FileBLOB(const File &f) 00245 : BLOB(), _filestream(), _file(f) 00246 { 00247 if (!f.exists()) 00248 { 00249 throw ibrcommon::FileNotExistsException(f); 00250 } 00251 } 00252 00253 FileBLOB::~FileBLOB() 00254 { 00255 } 00256 00257 void FileBLOB::open() 00258 { 00259 BLOB::_filelimit.wait(); 00260 00261 // open the file 00262 _filestream.open(_file.getPath().c_str(), ios::in|ios::binary); 00263 00264 if (!_filestream.is_open()) 00265 { 00266 throw ibrcommon::CanNotOpenFileException(_file); 00267 } 00268 } 00269 00270 void FileBLOB::close() 00271 { 00272 // close the file 00273 _filestream.close(); 00274 00275 BLOB::_filelimit.post(); 00276 } 00277 00278 size_t FileBLOB::__get_size() 00279 { 00280 return _file.size(); 00281 } 00282 00283 BLOB::Reference TmpFileBLOB::create() 00284 { 00285 try { 00286 return BLOB::Reference(new TmpFileBLOB()); 00287 } catch (const ibrcommon::IOException&) { 00288 return StringBLOB::create(); 00289 } 00290 } 00291 00292 void TmpFileBLOB::clear() 00293 { 00294 // close the file 00295 _filestream.close(); 00296 00297 // open temporary file 00298 _filestream.open(_tmpfile.getPath().c_str(), ios::in | ios::out | ios::trunc | ios::binary ); 00299 00300 if (!_filestream.is_open()) 00301 { 00302 IBRCOMMON_LOGGER(error) << "can not open temporary file " << _tmpfile.getPath() << IBRCOMMON_LOGGER_ENDL; 00303 throw ibrcommon::CanNotOpenFileException(_tmpfile); 00304 } 00305 } 00306 00307 TmpFileBLOB::TmpFileBLOB() 00308 : BLOB(), _filestream() 00309 { 00310 // check if path is a directory 00311 if (!BLOB::tmppath.isDirectory()) 00312 { 00313 throw ibrcommon::IOException("BLOB::tmppath not initialized or path is not a directory."); 00314 } 00315 00316 createtmpfile(); 00317 } 00318 00319 void TmpFileBLOB::createtmpfile() 00320 { 00321 _tmpfile = ibrcommon::TemporaryFile(BLOB::tmppath, "blob"); 00322 _tmpfile.update(); 00323 } 00324 00325 TmpFileBLOB::~TmpFileBLOB() 00326 { 00327 // delete the file if the last reference is destroyed 00328 _tmpfile.remove(); 00329 } 00330 00331 void TmpFileBLOB::open() 00332 { 00333 BLOB::_filelimit.wait(); 00334 00335 // open temporary file 00336 _filestream.open(_tmpfile.getPath().c_str(), ios::in | ios::out | ios::binary ); 00337 00338 if (!_filestream.is_open()) 00339 { 00340 IBRCOMMON_LOGGER(error) << "can not open temporary file " << _tmpfile.getPath() << IBRCOMMON_LOGGER_ENDL; 00341 throw ibrcommon::CanNotOpenFileException(_tmpfile); 00342 } 00343 } 00344 00345 void TmpFileBLOB::close() 00346 { 00347 // flush the filestream 00348 _filestream.flush(); 00349 00350 // close the file 00351 _filestream.close(); 00352 00353 BLOB::_filelimit.post(); 00354 } 00355 00356 size_t TmpFileBLOB::__get_size() 00357 { 00358 return _tmpfile.size(); 00359 } 00360 }