bes  Updated for version 3.17.4
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 const string BESUncompressCache::DIR_KEY = "BES.UncompressCache.dir";
35 const string BESUncompressCache::PREFIX_KEY = "BES.UncompressCache.prefix";
36 const string BESUncompressCache::SIZE_KEY = "BES.UncompressCache.size";
37 
38 unsigned long BESUncompressCache::getCacheSizeFromConfig()
39 {
40  bool found;
41  string size;
42  unsigned long size_in_megabytes = 0;
43  TheBESKeys::TheKeys()->get_value(SIZE_KEY, size, found);
44  if (found) {
45  std::istringstream iss(size);
46  iss >> size_in_megabytes;
47  }
48  else {
49  string msg = "[ERROR] BESUncompressCache::getCacheSize() - The BES Key " + SIZE_KEY
50  + " is not set! It MUST be set to utilize the decompression cache. ";
51  BESDEBUG("cache", msg << endl);
52  throw BESInternalError(msg, __FILE__, __LINE__);
53  }
54  return size_in_megabytes;
55 }
56 
57 string BESUncompressCache::getCacheDirFromConfig()
58 {
59  bool found;
60  string subdir = "";
61  TheBESKeys::TheKeys()->get_value(DIR_KEY, subdir, found);
62 
63  if (!found) {
64  string msg = "[ERROR] BESUncompressCache::getSubDirFromConfig() - The BES Key " + DIR_KEY
65  + " is not set! It MUST be set to utilize the decompression cache. ";
66  BESDEBUG("cache", msg << endl);
67  throw BESInternalError(msg, __FILE__, __LINE__);
68  }
69 
70  return subdir;
71 }
72 
73 string BESUncompressCache::getCachePrefixFromConfig()
74 {
75  bool found;
76  string prefix = "";
77  TheBESKeys::TheKeys()->get_value(PREFIX_KEY, prefix, found);
78  if (found) {
79  prefix = BESUtil::lowercase(prefix);
80  }
81  else {
82  string msg = "[ERROR] BESUncompressCache::getResultPrefix() - The BES Key " + PREFIX_KEY
83  + " is not set! It MUST be set to utilize the decompression cache. ";
84  BESDEBUG("cache", msg << endl);
85  throw BESInternalError(msg, __FILE__, __LINE__);
86  }
87 
88  return prefix;
89 }
90 
118 string BESUncompressCache::get_cache_file_name(const string &src, bool mangle)
119 {
120  string target = src;
121 
122  if (mangle) {
123  string::size_type last_dot = target.rfind('.');
124  if (last_dot != string::npos) {
125  target = target.substr(0, last_dot);
126  }
127  }
129 
130  BESDEBUG("cache", "BESFileLockingCache::get_cache_file_name - target: '" << target << "'" << endl);
131 
132  return target;
133 }
134 
135 BESUncompressCache::BESUncompressCache()
136 {
137  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - BEGIN" << endl);
138 
139  d_dimCacheDir = getCacheDirFromConfig();
140  d_dimCacheFilePrefix = getCachePrefixFromConfig();
141  d_maxCacheSize = getCacheSizeFromConfig();
142 
143  BESDEBUG("cache",
144  "BESUncompressCache() - Cache configuration params: " << d_dimCacheDir << ", " << d_dimCacheFilePrefix << ", " << d_maxCacheSize << endl);
145 
146  initialize(d_dimCacheDir, d_dimCacheFilePrefix, d_maxCacheSize);
147 
148  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - END" << endl);
149 
150 }
151 BESUncompressCache::BESUncompressCache(const string &data_root_dir, const string &cache_dir, const string &prefix,
152  unsigned long long size)
153 {
154 
155  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - BEGIN" << endl);
156 
157  d_dataRootDir = data_root_dir;
158  d_dimCacheDir = cache_dir;
159  d_dimCacheFilePrefix = prefix;
160  d_maxCacheSize = size;
161 
162  initialize(d_dimCacheDir, d_dimCacheFilePrefix, d_maxCacheSize);
163 
164  BESDEBUG("cache", "BESUncompressCache::BESUncompressCache() - END" << endl);
165 }
166 
168 BESUncompressCache::get_instance(const string &data_root_dir, const string &cache_dir, const string &result_file_prefix,
169  unsigned long long max_cache_size)
170 {
171  if (d_instance == 0) {
172  if (dir_exists(cache_dir)) {
173  try {
174  d_instance = new BESUncompressCache(data_root_dir, cache_dir, result_file_prefix, max_cache_size);
175 #ifdef HAVE_ATEXIT
176  atexit(delete_instance);
177 #endif
178  }
179  catch (BESInternalError &bie) {
180  BESDEBUG("cache",
181  "[ERROR] BESUncompressCache::get_instance(): Failed to obtain cache! msg: " << bie.get_message() << endl);
182  }
183  }
184  }
185  return d_instance;
186 }
187 
193 {
194  if (d_instance == 0) {
195  try {
196  d_instance = new BESUncompressCache();
197 #ifdef HAVE_ATEXIT
198  atexit(delete_instance);
199 #endif
200  }
201  catch (BESInternalError &bie) {
202  BESDEBUG("cache",
203  "[ERROR] BESUncompressCache::get_instance(): Failed to obtain cache! msg: " << bie.get_message() << endl);
204  }
205  }
206 
207  return d_instance;
208 }
209 
210 BESUncompressCache::~BESUncompressCache()
211 {
212  delete_instance();
213 }
214 
225 bool BESUncompressCache::is_valid(const string &cache_file_name, const string &local_id)
226 {
227  // If the cached response is zero bytes in size, it's not valid.
228  // (hmmm...)
229  string datasetFileName = BESUtil::assemblePath(d_dataRootDir, local_id, true);
230 
231  off_t entry_size = 0;
232  time_t entry_time = 0;
233  struct stat buf;
234  if (stat(cache_file_name.c_str(), &buf) == 0) {
235  entry_size = buf.st_size;
236  entry_time = buf.st_mtime;
237  }
238  else {
239  return false;
240  }
241 
242  if (entry_size == 0) return false;
243 
244  time_t dataset_time = entry_time;
245  if (stat(datasetFileName.c_str(), &buf) == 0) {
246  dataset_time = buf.st_mtime;
247  }
248 
249  // Trick: if the d_dataset is not a file, stat() returns error and
250  // the times stay equal and the code uses the cache entry.
251 
252  // TODO Fix this so that the code can get a LMT from the correct handler.
253  // TODO Consider adding a getLastModified() method to the libdap::DDS object to support this
254  // TODO The DDS may be expensive to instantiate - I think the handler may be a better location
255  // for an LMT method, if we can access the handler when/where needed.
256  if (dataset_time > entry_time) return false;
257 
258  return true;
259 }
260 
exception thrown if inernal error encountered
static string lowercase(const string &s)
Definition: BESUtil.cc:186
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:97
static string assemblePath(const string &firstPart, const string &secondPart, bool addLeadingSlash=false)
Assemble path fragments making sure that they are separated by a single &#39;/&#39; character.
Definition: BESUtil.cc:754
static bool dir_exists(const string &dir)
virtual string get_cache_file_name(const string &src, bool mangle=true)
Build the name of file that will holds the uncompressed data from &#39;src&#39; in the cache.
virtual string get_cache_file_name(const string &src, bool mangle=true)
static BESUncompressCache * get_instance()
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:483
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:43