bes  Updated for version 3.19.1
BESUncompressCache.cc
1 
2 // This file is part of bes, A C++ back-end server implementation framework
3 // for the OPeNDAP Data Access Protocol.
4 
5 // Copyright (c) 2015 OPeNDAP, Inc
6 // Author: Nathan Potter <npotter@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 
22 #include "BESUncompressCache.h"
23 #include <string>
24 #include <fstream>
25 #include <sstream>
26 #include <sys/stat.h>
27 
28 #include "BESInternalError.h"
29 #include "BESUtil.h"
30 #include "BESDebug.h"
31 #include "TheBESKeys.h"
32 
33 BESUncompressCache *BESUncompressCache::d_instance = 0;
34 bool BESUncompressCache::d_enabled = true;
35 
36 const string BESUncompressCache::DIR_KEY = "BES.UncompressCache.dir";
37 const string BESUncompressCache::PREFIX_KEY = "BES.UncompressCache.prefix";
38 const string BESUncompressCache::SIZE_KEY = "BES.UncompressCache.size";
39 
40 unsigned long BESUncompressCache::getCacheSizeFromConfig()
41 {
42  bool found;
43  string size;
44  unsigned long size_in_megabytes = 0;
45  TheBESKeys::TheKeys()->get_value(SIZE_KEY, size, found);
46  if (found) {
47  std::istringstream iss(size);
48  iss >> size_in_megabytes;
49  }
50  else {
51  string msg = "[ERROR] BESUncompressCache::getCacheSize() - The BES Key " + SIZE_KEY
52  + " is not set! It MUST be set to utilize the decompression cache. ";
53  BESDEBUG("cache", msg << endl);
54  throw BESInternalError(msg, __FILE__, __LINE__);
55  }
56  return size_in_megabytes;
57 }
58 
59 string BESUncompressCache::getCacheDirFromConfig()
60 {
61  bool found;
62  string subdir = "";
63  TheBESKeys::TheKeys()->get_value(DIR_KEY, subdir, found);
64 
65  if (!found) {
66  string msg = "[ERROR] BESUncompressCache::getSubDirFromConfig() - The BES Key " + DIR_KEY
67  + " is not set! It MUST be set to utilize the decompression cache. ";
68  BESDEBUG("cache", msg << endl);
69  throw BESInternalError(msg, __FILE__, __LINE__);
70  }
71 
72  return subdir;
73 }
74 
75 string BESUncompressCache::getCachePrefixFromConfig()
76 {
77  bool found;
78  string prefix = "";
79  TheBESKeys::TheKeys()->get_value(PREFIX_KEY, prefix, found);
80  if (found) {
81  prefix = BESUtil::lowercase(prefix);
82  }
83  else {
84  string msg = "[ERROR] BESUncompressCache::getResultPrefix() - The BES Key " + PREFIX_KEY
85  + " is not set! It MUST be set to utilize the decompression cache. ";
86  BESDEBUG("cache", msg << endl);
87  throw BESInternalError(msg, __FILE__, __LINE__);
88  }
89 
90  return prefix;
91 }
92 
120 string BESUncompressCache::get_cache_file_name(const string &src, bool mangle)
121 {
122  string target = src;
123 
124  if (mangle) {
125  string::size_type last_dot = target.rfind('.');
126  if (last_dot != string::npos) {
127  target = target.substr(0, last_dot);
128  }
129  }
131 
132  BESDEBUG("cache", "BESFileLockingCache::get_cache_file_name - target: '" << target << "'" << endl);
133 
134  return target;
135 }
136 
137 BESUncompressCache::BESUncompressCache()
138 {
139  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - BEGIN" << endl);
140 
141  d_enabled = true;
142  d_dimCacheDir = getCacheDirFromConfig();
143  d_dimCacheFilePrefix = getCachePrefixFromConfig();
144  d_maxCacheSize = getCacheSizeFromConfig();
145 
146  BESDEBUG("cache",
147  "BESUncompressCache() - Cache configuration params: " << d_dimCacheDir << ", " << d_dimCacheFilePrefix << ", " << d_maxCacheSize << endl);
148 
149  initialize(d_dimCacheDir, d_dimCacheFilePrefix, d_maxCacheSize);
150 
151  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - END" << endl);
152 
153 }
154 BESUncompressCache::BESUncompressCache(const string &data_root_dir, const string &cache_dir, const string &prefix,
155  unsigned long long size)
156 {
157  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - BEGIN" << endl);
158  d_enabled = true;
159 
160  d_dataRootDir = data_root_dir;
161  d_dimCacheDir = cache_dir;
162  d_dimCacheFilePrefix = prefix;
163  d_maxCacheSize = size;
164 
165  initialize(d_dimCacheDir, d_dimCacheFilePrefix, d_maxCacheSize);
166 
167  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - END" << endl);
168 }
169 
171 BESUncompressCache::get_instance(const string &data_root_dir, const string &cache_dir, const string &result_file_prefix,
172  unsigned long long max_cache_size)
173 {
174  if (d_enabled && d_instance == 0) {
175  if (dir_exists(cache_dir)) {
176  d_instance = new BESUncompressCache(data_root_dir, cache_dir, result_file_prefix, max_cache_size);
177  d_enabled = d_instance->cache_enabled();
178  if(!d_enabled){
179  delete d_instance;
180  d_instance = NULL;
181  BESDEBUG("cache", "BESUncompressCache::"<<__func__ << "() - " <<
182  "Cache is DISABLED"<< endl);
183  }
184  else {
185  #ifdef HAVE_ATEXIT
186  atexit(delete_instance);
187  #endif
188  BESDEBUG("cache", "BESUncompressCache::"<<__func__ << "() - " <<
189  "Cache is ENABLED"<< endl);
190  }
191  }
192  }
193  return d_instance;
194 }
195 
201 {
202  if (d_enabled && d_instance == 0) {
203  d_instance = new BESUncompressCache();
204  d_enabled = d_instance->cache_enabled();
205  if(!d_enabled){
206  delete d_instance;
207  d_instance = NULL;
208  BESDEBUG("cache", "BESUncompressCache::"<<__func__ << "() - " <<
209  "Cache is DISABLED"<< endl);
210  }
211  else {
212 #ifdef HAVE_ATEXIT
213  atexit(delete_instance);
214 #endif
215  BESDEBUG("cache", "BESUncompressCache::"<<__func__ << "() - " <<
216  "Cache is ENABLED"<< endl);
217  }
218  }
219 
220  return d_instance;
221 }
222 
223 BESUncompressCache::~BESUncompressCache()
224 {
225  delete_instance();
226 }
227 
238 bool BESUncompressCache::is_valid(const string &cache_file_name, const string &local_id)
239 {
240  // If the cached response is zero bytes in size, it's not valid.
241  // (hmmm...)
242  string datasetFileName = BESUtil::assemblePath(d_dataRootDir, local_id, true);
243 
244  off_t entry_size = 0;
245  time_t entry_time = 0;
246  struct stat buf;
247  if (stat(cache_file_name.c_str(), &buf) == 0) {
248  entry_size = buf.st_size;
249  entry_time = buf.st_mtime;
250  }
251  else {
252  return false;
253  }
254 
255  if (entry_size == 0) return false;
256 
257  time_t dataset_time = entry_time;
258  if (stat(datasetFileName.c_str(), &buf) == 0) {
259  dataset_time = buf.st_mtime;
260  }
261 
262  // Trick: if the d_dataset is not a file, stat() returns error and
263  // the times stay equal and the code uses the cache entry.
264 
265  // TODO Fix this so that the code can get a LMT from the correct handler.
266  // TODO Consider adding a getLastModified() method to the libdap::DDS object to support this
267  // TODO The DDS may be expensive to instantiate - I think the handler may be a better location
268  // for an LMT method, if we can access the handler when/where needed.
269  if (dataset_time > entry_time) return false;
270 
271  return true;
272 }
273 
exception thrown if inernal error encountered
static string lowercase(const string &s)
Definition: BESUtil.cc:189
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:422
static string assemblePath(const string &firstPart, const string &secondPart, bool addLeadingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
Definition: BESUtil.cc:757
static bool dir_exists(const string &dir)
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:62
virtual string get_cache_file_name(const string &src, bool mangle=true)
Build the name of file that will holds the uncompressed data from 'src' in the cache.
virtual string get_cache_file_name(const string &src, bool mangle=true)
static BESUncompressCache * get_instance()