openModeller  Version 1.5.0
RemoteRaster.cpp
Go to the documentation of this file.
1 
29 #include <openmodeller/Log.hh>
33 #include <openmodeller/Settings.hh>
34 
35 #include <vector>
36 #include <string.h>
37 #include <sstream>
38 #include <stdlib.h>
39 
40 using namespace std;
41 
42 #include <curl/curl.h>
43 
44 #ifdef MPI_FOUND
45 #include "mpi.h"
46 #endif
47 
48 /*************************************************************/
49 /*********************** Remote Raster ***********************/
50 
51 /******************/
52 /*** Destructor ***/
54 {
55 }
56 
57 /******************************/
58 /*** create Raster Callback ***/
59 Raster*
61 {
62  return new RemoteRaster();
63 }
64 
65 /*********************/
66 /*** create Raster ***/
67 void
68 RemoteRaster::createRaster( const string& str, int categ )
69 {
71  string cache_id = CacheManager::getContentIdMd5( str );
72 
73  // Check if file is being downloaded (presence of lock file)
74  std::string lock_file = cache_id;
75  lock_file.append(".lock");
76 
77  if ( CacheManager::isCached( lock_file, OM_REMOTE_RASTER_SUBDIR ) ) {
78 
79  Log::instance()->debug( "Ongoing concurrent download\n" );
80  throw RasterException( "Ongoing concurrent download", 1 );
81  }
82 
84 
85  Log::instance()->debug( "Layer %s already present in local cache (%s)\n", str.c_str(), cache_id.c_str() );
86 
87  GdalRaster::createRaster( cached_ref, categ );
88 
89  // TODO: send HEADER request to check if remote file has changed.
90  // (or always try to retrieve file anyway, but including If-Modified-Since and handling 304 code)
91  }
92  else {
93 
94  if ( isFromRejectedSource( str ) ) {
95 
96  std::string msg = "Untrusted source for remote raster. Aborting operation.\n";
97  Log::instance()->error( msg.c_str() );
98  throw RasterException( msg.c_str() );
99  }
100 
101  std::string retries_file = cache_id;
102  retries_file.append(".tries");
103 
104  std::string retries_fullpath = CacheManager::getContentLocation( retries_file, OM_REMOTE_RASTER_SUBDIR );
105  int num_retries = 0;
106 
107  // Check number of previous attempts
108  if ( CacheManager::isCached( retries_file, OM_REMOTE_RASTER_SUBDIR ) ) {
109 
110  fstream fin;
111  fin.open( retries_fullpath.c_str(), ios::in );
112 
113  if ( fin.is_open() ) {
114 
115  ostringstream oss;
116  string line;
117 
118  getline( fin, line );
119  oss << line << endl;
120 
121  // Note: if the content is empty, atoi returns 0
122  num_retries = atoi( oss.str().c_str() );
123 
124  fin.close();
125  }
126  else {
127 
128  throw RasterException( "Could not determine number of previous download retries." );
129  }
130 
131  if ( num_retries > 3 ) {
132 
134  throw RasterException( "Too many attempts to fetch raster. Aborting." );
135  }
136  }
137 
138  // Fetch file
139  try {
140 
141  // Last minute double check
142  if ( CacheManager::isCached( lock_file, OM_REMOTE_RASTER_SUBDIR ) ) {
143 
144  throw RasterException( "Ongoing concurrent download", 1 );
145  }
146 
147  // Create lock file
148  ostringstream oss (ostringstream::out);
149  CacheManager::cache( lock_file, oss, OM_REMOTE_RASTER_SUBDIR );
150 
151  // Increase number of retries
152  FILE *p_file = NULL;
153  p_file = fopen( retries_fullpath.c_str(), "w" );
154 
155  if ( p_file == NULL ) {
156 
157  // Could not open file
158  throw RasterException( "Could not store number of download retries." );
159  }
160  else {
161 
162  ++num_retries;
163  char buffer[2];
164  sprintf( buffer, "%d", num_retries );
165  fputs( buffer, p_file );
166  fclose( p_file );
167  }
168 
169  // Finally fetch raster
170  Log::instance()->debug( "Fetching remote raster %s (%s)...\n", str.c_str(), cache_id.c_str() );
171 
172  CURL *curl;
173 
174  static CacheFile file_data;
175  file_data.fileName = cached_ref.c_str();
176  file_data.stream = NULL;
177 
178  curl_global_init( CURL_GLOBAL_DEFAULT );
179 
180  curl = curl_easy_init();
181 
182  if ( curl ) {
183 
184  if ( CURLE_OK != curl_easy_setopt( curl, CURLOPT_URL, str.c_str() ) ) {
185 
186  std::string msg = "Could not configure remote raster fetcher.\n";
187  Log::instance()->error( msg.c_str() );
188  throw RasterException( msg.c_str() );
189  }
190 
191  // Enable following redirections
192  curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1 );
193  curl_easy_setopt( curl, CURLOPT_MAXREDIRS, 5 );
194 
195  // Timeout (30s)
196  curl_easy_setopt( curl, CURLOPT_TIMEOUT, 30 );
197 
198  /* NOSIGNAL should be set to true for timeout to work in multithread
199  * environments on Unix, requires libcurl 7.10 or more recent.
200  * (this force avoiding the use of signal handlers)
201  */
202 #ifdef CURLOPT_NOSIGNAL
203  curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 );
204 #endif
205 
206  curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, &RemoteRaster::_writeData );
207  curl_easy_setopt( curl, CURLOPT_WRITEDATA, &file_data );
208  //curl_easy_setopt( curl, CURLOPT_VERBOSE, 1 );
209 
210  CURLcode res = curl_easy_perform( curl );
211 
212  curl_easy_cleanup( curl );
213 
214  if ( file_data.stream ) {
215 
216  fclose( file_data.stream );
217  }
218 
219  curl_global_cleanup();
220 
221  if ( CURLE_OK != res ) {
222 
223  std::string msg = "Could not fetch remote raster.\n";
224  Log::instance()->error( msg.c_str() );
225  throw RasterException( msg.c_str() );
226  }
227  }
228  else {
229 
230  std::string msg = "Could not initialize remote raster fetcher.\n";
231  Log::instance()->error( msg.c_str() );
232  throw RasterException( msg.c_str() );
233  }
234 
235  Log::instance()->debug( "Done!\n", str.c_str(), cache_id.c_str() );
236 
237  GdalRaster::createRaster( cached_ref, categ );
238 
239  // Erase lock and retries
242  }
243  catch ( RasterException& e ) {
244 
245  if ( e.getCode() != 1 ) {
246 
247  // Erase lock
249  }
250 
251  throw;
252  }
253  catch (...) {
254 
255  // Erase lock
257  throw;
258  }
259  }
260 }
261 
262 #ifdef MPI_FOUND
263 void
264 RemoteRaster::createRaster( const string& output_file, const string& file, const MapFormat& format) {
265 
266  std::string msg = "Method createRaster() not available to create writable remote rasters.\n";
267  Log::instance()->error( msg.c_str() );
268  throw RasterException( msg.c_str() );
269 }
270 
271 #else
272 void
273 RemoteRaster::createRaster( const string& file, const MapFormat& format) {
274 
275  std::string msg = "Method createRaster() not available to create writable remote rasters.\n";
276  Log::instance()->warn( msg.c_str() );
277  throw RasterException( msg.c_str() );
278 }
279 #endif
280 
281 /***********/
282 /*** put ***/
283 int
285 {
286  std::string msg = "Method put() not available for remote rasters.\n";
287  Log::instance()->warn( msg.c_str() );
288  throw RasterException( msg.c_str() );
289 }
290 
291 /***********/
292 /*** put ***/
293 int
295 {
296  std::string msg = "Method put() not available for remote rasters.\n";
297  Log::instance()->warn( msg.c_str() );
298  throw RasterException( msg.c_str() );
299 }
300 
301 /**************/
302 /*** finish ***/
303 void
305 {
306  std::string msg = "Method finish() not available for remote rasters.\n";
307  Log::instance()->warn( msg.c_str() );
308  throw RasterException( msg.c_str() );
309 }
310 
311 /*********************/
312 /*** delete Raster ***/
313 int
315 {
316  std::string msg = "Method deleteRaster() not available for remote rasters.\n";
317  Log::instance()->warn( msg.c_str() );
318  throw RasterException( msg.c_str() );
319 }
320 
321 /*******************/
322 /*** _write Data ***/
323 size_t
324 RemoteRaster::_writeData( void *buffer, size_t size, size_t nmemb, void *stream ) {
325 
326  struct CacheFile *out = reinterpret_cast<CacheFile *>(stream);
327 
328  if ( out && !out->stream ) {
329 
330  out->stream = fopen( out->fileName, "wb" );
331 
332  if ( ! out->stream ) {
333 
334  return -1;
335  }
336  }
337 
338  return fwrite( buffer, size, nmemb, out->stream );
339 }
#define OM_REMOTE_RASTER_SUBDIR
Definition: RemoteRaster.hh:36
static size_t _writeData(void *buffer, size_t size, size_t nmemb, void *stream)
bool isFromRejectedSource(const std::string &str)
void warn(const char *format,...)
'Warn' level.
Definition: Log.cpp:273
static std::string getContentIdMd5(const std::string id)
static bool isCached(const std::string id, const std::string subdir="")
static void cache(const std::string id, const std::ostringstream &content, const std::string subdir="")
double Scalar
Type of map values.
Definition: om_defs.hh:39
static Raster * CreateRasterCallback()
FILE * stream
Definition: RemoteRaster.hh:47
static Log * instance()
Returns the instance pointer, creating the object on the first call.
Definition: Log.cpp:45
void createRaster(const std::string &str, int categ=0)
A common interface to rasters.
Definition: Raster.hh:44
static std::string getContentLocation(const std::string id, const std::string subdir="")
int put(Coord x, Coord y, Scalar val)
static int eraseCache(const std::string id, const std::string subdir="")
void error(const char *format,...)
'Error' level.
Definition: Log.cpp:290
const char * fileName
Definition: RemoteRaster.hh:46
static bool isCachedMd5(const std::string id, const std::string subdir="")
void createRaster(const std::string &file, int categ=0)
double Coord
Type of map coordinates.
Definition: om_defs.hh:38
static std::string getContentLocationMd5(const std::string id, const std::string subdir="")
void debug(const char *format,...)
'Debug' level.
Definition: Log.cpp:237