32#ifdef HAVE_TR1_FUNCTIONAL
33#include <tr1/functional>
39#include <libdap/DDS.h>
40#include <libdap/DMR.h>
41#include <libdap/DapXmlNamespaces.h>
42#include <libdap/ConstraintEvaluator.h>
43#include <libdap/DDXParserSAX2.h>
47#include <libdap/D4EnumDefs.h>
48#include <libdap/D4Dimensions.h>
49#include <libdap/D4Group.h>
51#include <libdap/D4ParserSax2.h>
58#ifdef DAP2_STORED_RESULTS
59#include <libdap/XDRStreamMarshaller.h>
60#include <libdap/XDRStreamUnMarshaller.h>
63#include <libdap/chunked_istream.h>
64#include <libdap/D4StreamUnMarshaller.h>
66#include <libdap/debug.h>
67#include <libdap/mime_util.h>
68#include <libdap/util.h>
70#include "BESStoredDapResultCache.h"
71#include "BESDapResponseBuilder.h"
72#include "BESInternalError.h"
75#include "TheBESKeys.h"
78#ifdef HAVE_TR1_FUNCTIONAL
79#define HASH_OBJ std::tr1::hash
81#define HASH_OBJ std::hash
85#define BES_DATA_ROOT "BES.Data.RootDirectory"
86#define BES_CATALOG_ROOT "BES.Catalog.catalog.RootDirectory"
93bool BESStoredDapResultCache::d_enabled =
true;
96const string BESStoredDapResultCache::SUBDIR_KEY =
"DAP.StoredResultsCache.subdir";
97const string BESStoredDapResultCache::PREFIX_KEY =
"DAP.StoredResultsCache.prefix";
98const string BESStoredDapResultCache::SIZE_KEY =
"DAP.StoredResultsCache.size";
101unsigned long BESStoredDapResultCache::getCacheSizeFromConfig()
105 unsigned long size_in_megabytes = 0;
108 istringstream iss(size);
109 iss >> size_in_megabytes;
113 msg <<
"[ERROR] BESStoredDapResultCache::getCacheSize() - The BES Key " << DAP_STORED_RESULTS_CACHE_SIZE_KEY;
114 msg <<
" is not set! It MUST be set to utilize the Stored Result Caching system. ";
115 BESDEBUG(
"cache", msg.str() << endl);
116 throw BESInternalError(msg.str(), __FILE__, __LINE__);
118 return size_in_megabytes;
121string BESStoredDapResultCache::getSubDirFromConfig()
129 msg <<
"[ERROR] BESStoredDapResultCache::getSubDirFromConfig() - The BES Key " << DAP_STORED_RESULTS_CACHE_SUBDIR_KEY;
130 msg <<
" is not set! It MUST be set to utilize the Stored Result Caching system. ";
131 BESDEBUG(
"cache", msg.str() << endl);
132 throw BESInternalError(msg.str(), __FILE__, __LINE__);
135 while (*subdir.begin() ==
'/' && !subdir.empty()) {
136 subdir = subdir.substr(1);
145string BESStoredDapResultCache::getResultPrefixFromConfig()
155 msg <<
"[ERROR] BESStoredDapResultCache::getResultPrefix() - The BES Key " << DAP_STORED_RESULTS_CACHE_PREFIX_KEY;
156 msg <<
" is not set! It MUST be set to utilize the Stored Result Caching system. ";
157 BESDEBUG(
"cache", msg.str() << endl);
158 throw BESInternalError(msg.str(), __FILE__, __LINE__);
164string BESStoredDapResultCache::getBesDataRootDirFromConfig()
167 string cacheDir =
"";
172 string msg = ((string)
"[ERROR] BESStoredDapResultCache::getStoredResultsDir() - Neither the BES Key ")
173 + BES_CATALOG_ROOT +
"or the BES key " + BES_DATA_ROOT
174 +
" have been set! One MUST be set to utilize the Stored Result Caching system. ";
175 BESDEBUG(
"cache", msg << endl);
176 throw BESInternalError(msg, __FILE__, __LINE__);
183BESStoredDapResultCache::BESStoredDapResultCache()
185 BESDEBUG(
"cache",
"BESStoredDapResultCache::BESStoredDapResultCache() - BEGIN" << endl);
187 d_storedResultsSubdir = getSubDirFromConfig();
188 d_dataRootDir = getBesDataRootDirFromConfig();
191 d_resultFilePrefix = getResultPrefixFromConfig();
192 d_maxCacheSize = getCacheSizeFromConfig();
195 "BESStoredDapResultCache() - Stored results cache configuration params: " << resultsDir <<
", " << d_resultFilePrefix <<
", " << d_maxCacheSize << endl);
197 initialize(resultsDir, d_resultFilePrefix, d_maxCacheSize);
199 BESDEBUG(
"cache",
"BESStoredDapResultCache::BESStoredDapResultCache() - END" << endl);
205BESStoredDapResultCache::BESStoredDapResultCache(
const string &data_root_dir,
const string &stored_results_subdir,
206 const string &result_file_prefix,
unsigned long long max_cache_size)
209 d_storedResultsSubdir = stored_results_subdir;
210 d_dataRootDir = data_root_dir;
211 d_resultFilePrefix = result_file_prefix;
212 d_maxCacheSize = max_cache_size;
218 const string &result_file_prefix,
unsigned long long max_cache_size)
220 if (d_enabled && d_instance == 0) {
222 d_instance =
new BESStoredDapResultCache(data_root_dir, stored_results_subdir, result_file_prefix,
224 d_enabled = d_instance->cache_enabled();
228 BESDEBUG(
"cache",
"BESStoredDapResultCache::"<<__func__ <<
"() - " <<
229 "Cache is DISABLED"<< endl);
233 atexit(delete_instance);
235 BESDEBUG(
"cache",
"BESStoredDapResultCache::"<<__func__ <<
"() - " <<
236 "Cache is ENABLED"<< endl);
249 if (d_enabled && d_instance == 0) {
250 d_instance =
new BESStoredDapResultCache();
251 d_enabled = d_instance->cache_enabled();
255 BESDEBUG(
"cache",
"BESStoredDapResultCache::"<<__func__ <<
"() - " <<
256 "Cache is DISABLED"<< endl);
260 atexit(delete_instance);
262 BESDEBUG(
"cache",
"BESStoredDapResultCache::"<<__func__ <<
"() - " <<
263 "Cache is ENABLED"<< endl);
279bool BESStoredDapResultCache::is_valid(
const string &cache_file_name,
const string &dataset)
284 off_t entry_size = 0;
285 time_t entry_time = 0;
287 if (stat(cache_file_name.c_str(), &buf) == 0) {
288 entry_size = buf.st_size;
289 entry_time = buf.st_mtime;
295 if (entry_size == 0)
return false;
297 time_t dataset_time = entry_time;
298 if (stat(dataset.c_str(), &buf) == 0) {
299 dataset_time = buf.st_mtime;
307 if (dataset_time > entry_time)
return false;
312#ifdef DAP2_STORED_RESULTS
324bool BESStoredDapResultCache::read_dap2_data_from_cache(
const string &cache_file_name, DDS *fdds)
327 "BESStoredDapResultCache::read_dap2_data_from_cache() - Opening cache file: " << cache_file_name << endl);
334 ifstream data(cache_file_name.c_str());
337 string mime = get_next_mime_header(data);
338 while (!mime.empty()) {
339 mime = get_next_mime_header(data);
343 DDXParser ddx_parser(fdds->get_factory());
346 string boundary = read_multipart_boundary(data);
348 "BESStoredDapResultCache::read_dap2_data_from_cache() - MPM Boundary: " << boundary << endl);
350 read_multipart_headers(data,
"text/xml", dods_ddx);
353 "BESStoredDapResultCache::read_dap2_data_from_cache() - Read the multipart haeaders" << endl);
359 ddx_parser.intern_stream(data, fdds, data_cid, boundary);
361 "BESStoredDapResultCache::read_dap2_data_from_cache() - Dataset name: " << fdds->get_dataset_name() << endl);
365 "BESStoredDapResultCache::read_dap2_data_from_cache() - DDX Parser Error: " << e.get_error_message() << endl);
371 "BESStoredDapResultCache::read_dap2_data_from_cache() - Data CID (before): " << data_cid << endl);
372 data_cid = cid_to_header_value(data_cid);
374 "BESStoredDapResultCache::read_dap2_data_from_cache() - Data CID (after): " << data_cid << endl);
378 read_multipart_headers(data,
"application/octet-stream", dods_data_ddx, data_cid);
383 XDRStreamUnMarshaller um(data);
384 for (DDS::Vars_iter i = fdds->var_begin(); i != fdds->var_end(); i++) {
385 (*i)->deserialize(um, fdds);
393 BESDEBUG(
"cache",
"BESStoredDapResultCache - The requested file does not exist. File: " + cache_file_name);
400 "BESStoredDapResultCache::read_dap4_data_from_cache() - caught exception, unlocking cache and re-throw." << endl);
419bool BESStoredDapResultCache::read_dap4_data_from_cache(
const string &cache_file_name, libdap::DMR *dmr)
421 BESDEBUG(
"cache",
"BESStoredDapResultCache::read_dap4_data_from_cache() - BEGIN" << endl);
428 "BESStoredDapResultCache::read_dap4_data_from_cache() - Opening cache file: " << cache_file_name << endl);
429 fstream in(cache_file_name.c_str(), ios::in | ios::binary);
440 chunked_istream cis(in, CHUNK_SIZE);
447 int chunk_size = cis.read_next_chunk();
450 "BESStoredDapResultCache::read_dap4_data_from_cache() - First chunk_size: " << chunk_size << endl);
452 if (chunk_size == EOF) {
453 throw InternalErr(__FILE__, __LINE__,
454 "BESStoredDapResultCache::read_dap4_data_from_cache() - Failed to read first chunk from file. Chunk size = EOF (aka "
455 + libdap::long_to_string(EOF) +
")");
459 char chunk[chunk_size];
460 cis.read(chunk, chunk_size);
461 BESDEBUG(
"cache",
"BESStoredDapResultCache::read_dap4_data_from_cache() - Read first chunk." << endl);
466 parser.intern(chunk, chunk_size - 2, dmr, debug);
467 BESDEBUG(
"cache",
"BESStoredDapResultCache::read_dap4_data_from_cache() - Parsed first chunk." << endl);
469 D4StreamUnMarshaller um(cis, cis.twiddle_bytes());
471 dmr->root()->deserialize(um, *dmr);
472 BESDEBUG(
"cache",
"BESStoredDapResultCache::read_dap4_data_from_cache() - Deserialized data." << endl);
474 BESDEBUG(
"cache",
"BESStoredDapResultCache::read_dap4_data_from_cache() - END" << endl);
483 BESDEBUG(
"cache",
"BESStoredDapResultCache - The requested file does not exist. File: " + cache_file_name);
491 "BESStoredDapResultCache::read_dap4_data_from_cache() - caught exception, unlocking cache and re-throw." << endl);
498#ifdef DAP2_STORED_RESULTS
504BESStoredDapResultCache::get_cached_dap2_data_ddx(
const string &cache_file_name,
BaseTypeFactory *factory,
505 const string &filename)
508 "BESStoredDapResultCache::get_cached_dap2_data_ddx() - Reading cache for " << cache_file_name << endl);
510 DDS *fdds =
new DDS(factory);
512 if (read_dap2_data_from_cache(cache_file_name, fdds)) {
514 fdds->filename(filename);
517 BESDEBUG(
"cache",
"DDS Filename: " << fdds->filename() << endl);
518 BESDEBUG(
"cache",
"DDS Dataset name: " << fdds->get_dataset_name() << endl);
520 fdds->set_factory(0);
524 DDS::Vars_iter i = fdds->var_begin();
525 while (i != fdds->var_end()) {
526 (*i)->set_read_p(
true);
527 (*i++)->set_send_p(
true);
546 const string &filename)
549 "BESStoredDapResultCache::get_cached_dap4_data() - Reading cache for " << cache_file_name << endl);
551 DMR *fdmr =
new DMR(factory);
553 BESDEBUG(
"cache",
"BESStoredDapResultCache::get_cached_dap4_data() - DMR Filename: " << fdmr->filename() << endl);
554 fdmr->set_filename(filename);
556 if (read_dap4_data_from_cache(cache_file_name, fdmr)) {
558 "BESStoredDapResultCache::get_cached_dap4_data() - DMR Dataset name: " << fdmr->name() << endl);
560 fdmr->set_factory(0);
564 fdmr->root()->set_send_p(
true);
565 fdmr->root()->set_read_p(
true);
573#ifdef DAP2_STORED_RESULTS
578string BESStoredDapResultCache::store_dap2_result(DDS &dds,
const string &constraint,
BESDapResponseBuilder *rb,
579 ConstraintEvaluator *eval)
581 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap2_result() - BEGIN" << endl);
587 string local_id = get_stored_result_local_id(dds.filename(), constraint, DAP_3_2);
588 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap2_result() - local_id: "<< local_id << endl);
590 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap2_result() - cache_file_name: "<< cache_file_name << endl);
597 if (!is_valid(cache_file_name, dds.filename()))
purge_file(cache_file_name);
601 "BESStoredDapResultCache::store_dap2_result() - Stored Result already exists. Not rewriting file: " << cache_file_name << endl);
607 "BESStoredDapResultCache::store_dap2_result() - cache_file_name " << cache_file_name <<
", constraint: " << constraint << endl);
613 eval->parse_constraint(constraint, *fdds);
615 if (eval->function_clauses()) {
616 DDS *temp_fdds = eval->eval_function_clauses(*fdds);
622 ofstream data_stream(cache_file_name.c_str());
624 throw InternalErr(__FILE__, __LINE__,
625 "Could not open '" + cache_file_name +
"' to write cached response.");
627 string start =
"dataddx_cache_start", boundary =
"dataddx_cache_boundary";
631 ConstraintEvaluator eval;
635 dds.set_dap_version(
"3.2");
642 set_mime_multipart(data_stream, boundary, start, dods_data_ddx, x_plain,
645 rb->serialize_dap2_data_ddx(data_stream, (DDS**) &dds, eval, boundary, start);
648 data_stream << CRLF <<
"--" << boundary <<
"--" << CRLF;
668 "BESStoredDapResultCache::store_dap2_result() - Stored Result already exists. Not rewriting file: " << cache_file_name << endl);
671 throw InternalErr(__FILE__, __LINE__,
672 "BESStoredDapResultCache::store_dap2_result() - Cache error during function invocation.");
676 "BESStoredDapResultCache::store_dap2_result() - unlocking and closing cache file "<< cache_file_name << endl);
681 "BESStoredDapResultCache::store_dap2_result() - caught exception, unlocking cache and re-throw." << endl);
687 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap2_result() - END (local_id=`"<< local_id <<
"')" << endl);
699string BESStoredDapResultCache::get_stored_result_local_id(
const string &dataset,
const string &ce,
700 libdap::DAPVersion version)
702 BESDEBUG(
"cache",
"get_stored_result_local_id() - BEGIN. dataset: " << dataset <<
", ce: " << ce << endl);
703 std::ostringstream ostr;
704 HASH_OBJ<std::string> str_hash;
705 string name = dataset +
"#" + ce;
706 ostr << str_hash(name);
707 string hashed_name = ostr.str();
708 BESDEBUG(
"cache",
"get_stored_result_local_id() - hashed_name: " << hashed_name << endl);
712#ifdef DAP2_STORED_RESULTS
718 suffix =
".data_ddx";
726 throw BESInternalError(
"BESStoredDapResultCache::get_stored_result_local_id() - Unrecognized DAP version!!",
731 BESDEBUG(
"cache",
"get_stored_result_local_id() - Data file suffix: " << suffix << endl);
733 string local_id = d_resultFilePrefix + hashed_name + suffix;
734 BESDEBUG(
"cache",
"get_stored_result_local_id() - file: " << local_id << endl);
738 BESDEBUG(
"cache",
"get_stored_result_local_id() - END. local_id: " << local_id << endl);
748 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap4_result() - BEGIN" << endl);
750 BaseTypeFactory factory;
754 string local_id = get_stored_result_local_id(dmr.filename(), constraint, DAP_4_0);
755 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap4_result() - local_id: "<< local_id << endl);
757 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap4_result() - cache_file_name: "<< cache_file_name << endl);
766 if (!is_valid(cache_file_name, dmr.filename())) {
768 "BESStoredDapResultCache::store_dap4_result() - File is not valid. Purging file from cache. filename: " << cache_file_name << endl);
774 "BESStoredDapResultCache::store_dap4_result() - Stored Result already exists. Not rewriting file: " << cache_file_name << endl);
780 "BESStoredDapResultCache::store_dap4_result() - cache_file_name: " << cache_file_name <<
", constraint: " << constraint << endl);
782 ofstream data_stream(cache_file_name.c_str());
784 throw InternalErr(__FILE__, __LINE__,
785 "Could not open '" + cache_file_name +
"' to write cached response.");
808 "BESStoredDapResultCache::store_dap4_result() - Couldn't create and lock file, But I got a read lock. " "Result may have been created by another process. " "Not rewriting file: " << cache_file_name << endl);
811 throw InternalErr(__FILE__, __LINE__,
812 "BESStoredDapResultCache::store_dap4_result() - Cache error during function invocation.");
816 "BESStoredDapResultCache::store_dap4_result() - unlocking and closing cache file "<< cache_file_name << endl);
822 "BESStoredDapResultCache::store_dap4_result() - caught exception, unlocking cache and re-throw." << endl);
830 BESDEBUG(
"cache",
"BESStoredDapResultCache::store_dap4_result() - END (local_id=`"<< local_id <<
"')" << endl);
virtual std::string get_dataset_name() const
Get the dataset name.
virtual void serialize_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers=true)
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
void initialize(const std::string &cache_dir, const std::string &prefix, unsigned long long size)
Initialize an instance of FileLockingCache.
virtual void unlock_and_close(const std::string &target)
virtual unsigned long long update_cache_info(const std::string &target)
Update the cache info file to include 'target'.
virtual bool create_and_lock(const std::string &target, int &fd)
Create a file in the cache and lock it for write access.
virtual void exclusive_to_shared_lock(int fd)
Transfer from an exclusive lock to a shared lock.
virtual bool get_read_lock(const std::string &target, int &fd)
Get a read-only lock on the file if it exists.
static bool dir_exists(const std::string &dir)
virtual void purge_file(const std::string &file)
Purge a single file from the cache.
virtual bool cache_too_big(unsigned long long current_size) const
look at the cache size; is it too large? Look at the cache size and see if it is too big.
virtual void update_and_purge(const std::string &new_file)
Purge files from the cache.
virtual std::string get_cache_file_name(const std::string &src, bool mangle=true)
virtual string store_dap4_result(libdap::DMR &dmr, const string &constraint, BESDapResponseBuilder *rb)
libdap::DMR * get_cached_dap4_data(const string &cache_file_name, libdap::D4BaseTypeFactory *factory, const string &filename)
static BESStoredDapResultCache * get_instance()
static std::string lowercase(const std::string &s)
static std::string assemblePath(const std::string &firstPart, const std::string &secondPart, bool leadingSlash=false, bool trailingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
static TheBESKeys * TheKeys()
Access to the singleton.