openModeller  Version 1.5.0
WcsProxyRaster.cpp
Go to the documentation of this file.
1 
28 #include <openmodeller/Log.hh>
32 #include <openmodeller/Settings.hh>
33 
34 #include <gdal.h>
35 
36 #include <string.h>
37 #include <sstream>
38 #include <algorithm>
39 
40 using namespace std;
41 
42 #ifdef MPI_FOUND
43 #include "mpi.h"
44 #endif
45 
46 /*********************************************************/
47 /*********************** Functions ***********************/
48 
49 /*******************************/
50 /*** is From Rejected Source ***/
51 bool
52 isFromRejectedSource( const std::string& str ) {
53 
54  // Remote sources are untrusted by default
55 
56  if ( Settings::count( "ALLOW_RASTER_SOURCE" ) > 0 ) {
57 
58  // get host from url
59  string host;
60  string lower_url;
61  transform( str.begin(), str.end(), std::back_inserter(lower_url), ::tolower );
62 
63  if ( str.size() < 9 ) { // ftp://x.x
64 
65  std::string msg = "Invalid identifier for remote raster (1).\n";
66  Log::instance()->error( msg.c_str() );
67  throw RasterException( msg.c_str() );
68  }
69 
70  size_t prot_i = str.find("://");
71 
72  if ( prot_i == string::npos ) {
73 
74  std::string msg = "Missing protocol in remote raster identifier.\n";
75  Log::instance()->error( msg.c_str() );
76  throw RasterException( msg.c_str() );
77  }
78 
79  size_t path_i = str.find("/", prot_i+3);
80 
81  if ( path_i == string::npos ) {
82 
83  // There must be at least one slash in the identifier!
84  std::string msg = "Invalid identifier for remote raster (2).\n";
85  Log::instance()->error( msg.c_str() );
86  throw RasterException( msg.c_str() );
87  }
88 
89  host = lower_url.substr( prot_i+3, path_i - (prot_i+3) );
90 
91  size_t port_i = host.find(":");
92 
93  if ( port_i != string::npos ) {
94 
95  // Ignore port
96  host = host.substr( 0, port_i );
97  }
98 
99  vector<string> accepted_sources = Settings::getAll( "ALLOW_RASTER_SOURCE" );
100 
101  for( unsigned int i = 0; i < accepted_sources.size(); i++ ) {
102 
103  // This is how you can accept any source (don't do that)
104  if ( accepted_sources[i].compare("*") == 0 ) {
105 
106  return false;
107  }
108 
109  size_t pos = host.find( accepted_sources[i] );
110 
111  if ( pos == string::npos ) {
112 
113  continue;
114  }
115 
116  if ( pos == host.size() - accepted_sources[i].size() ) {
117 
118  // Configured source must match the end of the host
119  return false;
120  }
121  }
122  }
123 
124  return true;
125 }
126 
127 /****************************************************************/
128 /*********************** WCS Proxy Raster ***********************/
129 
130 /******************/
131 /*** Destructor ***/
133 {
134 }
135 
136 /******************************/
137 /*** create Raster Callback ***/
138 Raster*
140 {
141  return new WcsProxyRaster();
142 }
143 
144 /*********************/
145 /*** create Raster ***/
146 void
147 WcsProxyRaster::createRaster( const string& str, int categ )
148 {
149  string cache_id = CacheManager::getContentIdMd5( str );
150 
152 
154 
155  Log::instance()->debug( "WCS proxy raster %s already present in local cache (%s)\n", str.c_str(), cache_id.c_str() );
156  }
157  else {
158 
159  Log::instance()->debug( "Setting up WCS proxy for %s (%s)\n", str.c_str(), cache_id.c_str() );
160 
161  // Extract tokens from identifier
162  vector<string> tokens;
163  stringstream ss(str);
164  string token;
165  while ( getline(ss, token, '>') ) {
166 
167  tokens.push_back(token);
168  }
169 
170  if ( tokens.size() != 3 ) {
171 
172  std::string msg = "Invalid WCS identifier. Make sure it has 3 parts separated by \">\".\n";
173  Log::instance()->error( msg.c_str() );
174  throw RasterException( msg.c_str() );
175  }
176 
177  if ( isFromRejectedSource( tokens[1] ) ) {
178 
179  std::string msg = "Untrusted source for WCS raster. Aborting operation.\n";
180  Log::instance()->error( msg.c_str() );
181  throw RasterException( msg.c_str() );
182  }
183 
184  // Create GDAL WCS XML file
185  ostringstream oss (ostringstream::out);
186  oss<<"<WCS_GDAL>"<<std::endl;
187  oss<<"<ServiceURL>"<<tokens[1]<<"</ServiceURL>"<<std::endl;
188  oss<<"<CoverageName>"<<tokens[2]<<"</CoverageName>"<<std::endl;
189  oss<<"</WCS_GDAL>";
190 
191  // Then cache it
193  }
194 
195  string cached_ref = CacheManager::getContentLocationMd5( str, OM_WCS_PROXY_SUBDIR );
196 
198  if ( Settings::count( "FETCH_WCS" ) == 1 && Settings::get( "FETCH_WCS" ) == "true" ) {
199 
200  Log::instance()->debug( "openModeller configured to work with local copies of WCS rasters\n" );
201 
202  std::string clone_id( cache_id );
203  clone_id.append(".img");
204 
205  std::string clone_ref( cached_ref );
206  clone_ref.append(".img");
207 
208  // Check if file is being downloaded (presence of lock file)
209  std::string lock_file = cache_id;
210  lock_file.append(".lock");
211 
212  if ( CacheManager::isCached( lock_file, OM_WCS_PROXY_SUBDIR ) ) {
213 
214  Log::instance()->debug( "Ongoing concurrent download\n" );
215  throw RasterException( "Ongoing concurrent download", 1 );
216  }
217 
218  if ( CacheManager::isCached( clone_id, OM_WCS_PROXY_SUBDIR ) ) {
219 
220  Log::instance()->debug( "Local WCS copy already present: %s\n", clone_ref.c_str() );
221 
222  GdalRaster::createRaster( clone_ref, categ );
223  }
224  else {
225 
226  Log::instance()->debug( "Local WCS copy does not exist\n" );
227 
228  std::string retries_file = cache_id;
229  retries_file.append(".tries");
230 
231  std::string retries_fullpath = CacheManager::getContentLocation( retries_file, OM_WCS_PROXY_SUBDIR );
232  int num_retries = 0;
233 
234  // Check number of previous attempts
235  if ( CacheManager::isCached( retries_file, OM_WCS_PROXY_SUBDIR ) ) {
236 
237  fstream fin;
238  fin.open( retries_fullpath.c_str(), ios::in );
239 
240  if ( fin.is_open() ) {
241 
242  ostringstream oss;
243  string line;
244 
245  getline( fin, line );
246  oss << line << endl;
247 
248  // Note: if the content is empty, atoi returns 0
249  num_retries = atoi( oss.str().c_str() );
250 
251  fin.close();
252  }
253  else {
254 
255  throw RasterException( "Could not determine number of previous download retries." );
256  }
257 
258  if ( num_retries > 3 ) {
259 
260  // Without removing the file, it will need to be manually removed before trying again!
262  throw RasterException( "Too many attempts to fetch raster. Aborting." );
263  }
264  }
265 
266  // Fetch file
267  try {
268 
269  // Last minute double check
270  if ( CacheManager::isCached( lock_file, OM_WCS_PROXY_SUBDIR ) ) {
271 
272  throw RasterException( "Ongoing concurrent download", 1 );
273  }
274 
275  // Create lock file
276  ostringstream oss (ostringstream::out);
277  CacheManager::cache( lock_file, oss, OM_WCS_PROXY_SUBDIR );
278 
279  // Increase number of retries
280  FILE *p_file = NULL;
281  p_file = fopen( retries_fullpath.c_str(), "w" );
282 
283  if ( p_file == NULL ) {
284 
285  // Could not open file
286  throw RasterException( "Could not store number of download retries." );
287  }
288  else {
289 
290  ++num_retries;
291  char buffer[2];
292  sprintf( buffer, "%d", num_retries );
293  fputs( buffer, p_file );
294  fclose( p_file );
295  }
296 
297  // Finally fetch raster
298  Log::instance()->debug( "Fetching WCS raster...\n" );
299 
300  GDALAllRegister();
301 
302  // Open source raster
303  GDALDatasetH hDataset = GDALOpen( cached_ref.c_str(), GA_ReadOnly );
304  if ( hDataset == NULL ) {
305 
306  std::string msg = "Failed to open WCS raster!\n";
307  Log::instance()->error( msg.c_str() );
308  throw RasterException( msg.c_str() );
309  }
310 
311  GDALDriverH hDriver = GDALGetDriverByName( "HFA" ); // Use Erdas Imagine
312  if ( hDriver == NULL ) {
313 
314  GDALClose( hDataset );
315  GDALDestroyDriverManager();
316 
317  std::string msg = "Could not find GDAL HFA driver!\n";
318  Log::instance()->error( msg.c_str() );
319  throw RasterException( msg.c_str() );
320  }
321 
322  // Copy WCS proxy to HFA
323  GDALDatasetH hOutDS = GDALCreateCopy( hDriver, clone_ref.c_str(), hDataset, FALSE, NULL, NULL, NULL );
324 
325  if ( hOutDS == NULL ) {
326 
327  GDALClose( hDataset );
328  GDALDestroyDriverManager();
329 
330  std::string msg = "Could not clone WCS raster!\n";
331  Log::instance()->error( msg.c_str() );
332  throw RasterException( msg.c_str() );
333  }
334 
335  GDALClose( hOutDS );
336  GDALClose( hDataset );
337 
338  Log::instance()->debug( "Done!\n" );
339 
340  GdalRaster::createRaster( clone_ref, categ );
341 
342  // Erase lock and retries
345  }
346  catch ( RasterException& e ) {
347 
348  if ( e.getCode() != 1 ) {
349 
350  // Erase lock
352  }
353 
354  throw;
355  }
356  catch (...) {
357 
358  // Erase lock
360  throw;
361  }
362  }
363  }
364  else {
365 
366  Log::instance()->debug( "openModeller configured to work with remote WCS rasters\n" );
367 
368  GdalRaster::createRaster( cached_ref, categ );
369  }
370 }
371 
372 #ifdef MPI_FOUND
373 void
374 WcsProxyRaster::createRaster( const string& output_file, const string& file, const MapFormat& format) {
375 
376  std::string msg = "Method createRaster() not available to create writable WCS rasters.\n";
377  Log::instance()->error( msg.c_str() );
378  throw RasterException( msg.c_str() );
379 }
380 
381 #else
382 void
383 WcsProxyRaster::createRaster( const string& file, const MapFormat& format) {
384 
385  std::string msg = "Method createRaster() not available to create writable WCS rasters.\n";
386  Log::instance()->warn( msg.c_str() );
387  throw RasterException( msg.c_str() );
388 }
389 #endif
390 
391 /***********/
392 /*** put ***/
393 int
395 {
396  std::string msg = "Method put() not available for WCS rasters.\n";
397  Log::instance()->warn( msg.c_str() );
398  throw RasterException( msg.c_str() );
399 }
400 
401 /***********/
402 /*** put ***/
403 int
405 {
406  std::string msg = "Method put() not available for WCS rasters.\n";
407  Log::instance()->warn( msg.c_str() );
408  throw RasterException( msg.c_str() );
409 }
410 
411 /**************/
412 /*** finish ***/
413 void
415 {
416  std::string msg = "Method finish() not available for WCS rasters.\n";
417  Log::instance()->warn( msg.c_str() );
418  throw RasterException( msg.c_str() );
419 }
420 
421 /*********************/
422 /*** delete Raster ***/
423 int
425 {
426  std::string msg = "Method deleteRaster() not available for WCS rasters.\n";
427  Log::instance()->warn( msg.c_str() );
428  throw RasterException( msg.c_str() );
429 }
static std::vector< std::string > getAll(const std::string &key)
Definition: Settings.cpp:142
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="")
static void cacheMd5(const std::string id, const std::ostringstream &content, const std::string subdir="")
double Scalar
Type of map values.
Definition: om_defs.hh:39
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="")
#define OM_WCS_PROXY_SUBDIR
int put(Coord x, Coord y, Scalar val)
static std::string get(const std::string &key)
Definition: Settings.cpp:112
static int eraseCache(const std::string id, const std::string subdir="")
void error(const char *format,...)
'Error' level.
Definition: Log.cpp:290
static bool isCachedMd5(const std::string id, const std::string subdir="")
static Raster * CreateRasterCallback()
static int count(const std::string &key)
Definition: Settings.cpp:127
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