OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESUncompressManager.cc
Go to the documentation of this file.
00001 // BESUncompressManager.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include <sstream>
00034 
00035 using std::istringstream ;
00036 
00037 #include "BESUncompressManager.h"
00038 #include "BESUncompressGZ.h"
00039 #include "BESUncompressBZ2.h"
00040 #include "BESUncompressZ.h"
00041 #include "BESCache.h"
00042 #include "BESInternalError.h"
00043 #include "BESDebug.h"
00044 #include "TheBESKeys.h"
00045 
00046 BESUncompressManager *BESUncompressManager::_instance = 0 ;
00047 
00057 BESUncompressManager::BESUncompressManager()
00058 {
00059     add_method( "gz", BESUncompressGZ::uncompress ) ;
00060     add_method( "bz2", BESUncompressBZ2::uncompress ) ;
00061     add_method( "z", BESUncompressZ::uncompress ) ;
00062 
00063     bool found = false ;
00064     string key = "BES.Uncompress.Retry" ;
00065     string val ;
00066     TheBESKeys::TheKeys()->get_value( key, val, found ) ;
00067     if( !found || val.empty() )
00068     {
00069         _retry = 2000 ;
00070     }
00071     else
00072     {
00073         istringstream is( val ) ;
00074         is >> _retry ;
00075     }
00076 
00077     key = "BES.Uncompress.NumTries" ;
00078     val = "" ;
00079     TheBESKeys::TheKeys()->get_value( key, val, found ) ;
00080     if( !found || val.empty() )
00081     {
00082         _num_tries = 10 ;
00083     }
00084     else
00085     {
00086         istringstream is( val ) ;
00087         is >> _num_tries ;
00088     }
00089 }
00090 
00100 bool
00101 BESUncompressManager::add_method( const string &name,
00102                                   p_bes_uncompress method )
00103 {
00104     BESUncompressManager::UCIter i ;
00105     i = _uncompress_list.find( name ) ;
00106     if( i == _uncompress_list.end() )
00107     {
00108         _uncompress_list[name] = method ;
00109         return true ;
00110     }
00111     return false ;
00112 }
00113 
00122 bool
00123 BESUncompressManager::remove_method( const string &name )
00124 {
00125     BESUncompressManager::UIter i ;
00126     i = _uncompress_list.find( name ) ;
00127     if( i != _uncompress_list.end() )
00128     {
00129         _uncompress_list.erase( i ) ;
00130         return true ;
00131     }
00132     return false ;
00133 }
00134 
00143 p_bes_uncompress
00144 BESUncompressManager::find_method( const string &name )
00145 {
00146     BESUncompressManager::UCIter i ;
00147     i = _uncompress_list.find( name ) ;
00148     if( i != _uncompress_list.end() )
00149     {
00150         return (*i).second ;
00151     }
00152     return 0 ;
00153 }
00154 
00160 string
00161 BESUncompressManager::get_method_names()
00162 {
00163     string ret ;
00164     bool first_name = true ;
00165     BESUncompressManager::UCIter i = _uncompress_list.begin() ;
00166     for( ; i != _uncompress_list.end(); i++ )
00167     {
00168         if( !first_name )
00169             ret += ", " ;
00170         ret += (*i).first ;
00171         first_name = false ;
00172     }
00173     return ret ;
00174 }
00175 
00212 bool
00213 BESUncompressManager::uncompress( const string &src, string &target,
00214                                   BESCache &cache )
00215 {
00216     BESDEBUG( "bes", "BESUncompressManager::uncompress - src = " << src << endl ) ;
00217     string::size_type dot = src.rfind( "." ) ;
00218     if( dot != string::npos )
00219     {
00220         string ext = src.substr( dot+1, src.length() - dot ) ;
00221         // Why fold the extension to lowercase? jhrg 5/9/07
00222         // The extension (Z, gz, bz2, GZ, BZ2, z) is used to determine which
00223         // uncompression engine to use. It is compared to the list, which is
00224         // all lower case. pcw 2/22/08
00225         for( int i = 0; i < static_cast<int>(ext.length()); i++ )
00226         {
00227             ext[i] = tolower( ext[i] ) ;
00228         }
00229 
00230         // if we find the method for this file then use it. If we don't find
00231         // it then assume that the file is not compressed and simply return
00232         // the src file at the end of the method.
00233         p_bes_uncompress p = find_method( ext ) ;
00234         if( p )
00235         {
00236             // the file is compressed so we either need to uncompress it or
00237             // we need to tell if it is already cached. To do this, lock the
00238             // cache so no one else can do anything
00239             if( cache.lock( _retry, _num_tries ) )
00240             {
00241                 try
00242                 {
00243                     // before calling uncompress on the file, see if the file
00244                     // has already been cached. If it has, then simply return
00245                     // the target, no need to cache.
00246                     BESDEBUG( "bes", "BESUncompressManager::uncompress - is cached? " << src << endl ) ;
00247                     if( cache.is_cached( src, target ) )
00248                     {
00249                         BESDEBUG( "bes", "BESUncompressManager::uncompress - "  << "is cached " << target << endl ) ;
00250                         cache.unlock() ;
00251                         return true ;
00252                     }
00253 
00254                     // the file is not cached, so we need to uncompress the
00255                     // file.  First determine if there is enough space in
00256                     // the cache to uncompress the file
00257                     BESDEBUG( "bes", "BESUncompressManager::uncompress - " << "purging cache" << endl ) ;
00258                     cache.purge() ;
00259 
00260                     // Now that we have some room ... uncompress the file
00261                     BESDEBUG( "bes", "BESUncompressManager::uncompress - "
00262                                      << "uncompress to " << target
00263                                      << " using " << ext << " uncompression"
00264                                      << endl ) ;
00265 
00266                     // we are now done in the cache, unlock it
00267                     cache.unlock() ;
00268 
00269                     // MPJ: Is this safe to call after unlock?
00270                     // We just unlocked the cache before we
00271                     // decompress the file, so the is_cached call may fail
00272                     // for another call while this is occurring
00273                     // and spawn another decompress overwriting the other?
00274                     // Or will another coming along see the unfinished
00275                     // decompressed file and complain?
00276                     p( src, target ) ;
00277                     return true ;
00278                 }
00279                 catch( BESError & )
00280                 {
00281                     // a problem in the cache, unlock it and re-throw the
00282                     // exception
00283                     cache.unlock() ;
00284                     throw ;
00285                 }
00286                 catch( ... )
00287                 {
00288                     // an unknown problem in the cache, unlock it and throw a
00289                     // BES exception
00290                     cache.unlock() ;
00291                     string err = (string)"Problem working with the cache, "
00292                                  + "unknown error" ;
00293                     throw BESInternalError( err, __FILE__,__LINE__);
00294                 }
00295             }
00296             else
00297             {
00298                 string err = "Unable to lock the cache " 
00299                              + cache.cache_dir() ;
00300                 throw BESInternalError( err, __FILE__, __LINE__ ) ;
00301             }
00302         }
00303         else
00304         {
00305             BESDEBUG( "bes", "BESUncompressManager::uncompress - not compressed " << endl ) ;
00306         }
00307     }
00308     else
00309     {
00310         BESDEBUG( "bes", "BESUncompressmanager::uncompress - not file extension" << endl ) ;
00311 #if 0
00312         // This could just mean that there is a README file here, so just
00313         // return the src file name and let the system run its course.
00314         string err = "Unable to determine type of file from "
00315                      + src ;
00316         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00317 #endif
00318     }
00319 
00320     return false ;
00321 }
00322 
00330 void
00331 BESUncompressManager::dump( ostream &strm ) const
00332 {
00333     strm << BESIndent::LMarg << "BESUncompressManager::dump - ("
00334                              << (void *)this << ")" << endl ;
00335     BESIndent::Indent() ;
00336     if( _uncompress_list.size() )
00337     {
00338         strm << BESIndent::LMarg << "registered uncompression methods:" << endl;
00339         BESIndent::Indent() ;
00340         BESUncompressManager::UCIter i = _uncompress_list.begin() ;
00341         BESUncompressManager::UCIter ie = _uncompress_list.end() ;
00342         for( ; i != ie; i++ )
00343         {
00344             strm << BESIndent::LMarg << (*i).first << endl ;
00345         }
00346         BESIndent::UnIndent() ;
00347     }
00348     else
00349     {
00350         strm << BESIndent::LMarg << "registered uncompress methods: none" << endl ;
00351     }
00352     BESIndent::UnIndent() ;
00353 }
00354 
00355 BESUncompressManager *
00356 BESUncompressManager::TheManager()
00357 {
00358     if( _instance == 0 )
00359     {
00360         _instance = new BESUncompressManager ;
00361     }
00362     return _instance ;
00363 }