39#ifdef HAVE_TR1_FUNCTIONAL
40#include <tr1/functional>
43#include <libdap/DDS.h>
44#include <libdap/ConstraintEvaluator.h>
45#include <libdap/DDXParserSAX2.h>
47#include <libdap/XDRStreamMarshaller.h>
48#include <libdap/XDRStreamUnMarshaller.h>
49#include <libdap/XDRFileUnMarshaller.h>
51#include <libdap/D4StreamMarshaller.h>
52#include <libdap/D4StreamUnMarshaller.h>
54#include <libdap/Sequence.h>
56#include <libdap/debug.h>
57#include <libdap/mime_util.h>
58#include <libdap/util.h>
60#include "CacheTypeFactory.h"
61#include "CacheMarshaller.h"
62#include "CacheUnMarshaller.h"
64#include "BESDapFunctionResponseCache.h"
65#include "BESDapResponseBuilder.h"
66#include "BESInternalError.h"
69#include "TheBESKeys.h"
73#define DEBUG_KEY "response_cache"
75#ifdef HAVE_TR1_FUNCTIONAL
76#define HASH_OBJ std::tr1::hash
78#define HASH_OBJ std::hash
84const string DATA_MARK =
"--DATA:";
87const unsigned int max_cacheable_ce_len = 4096;
88const unsigned int max_collisions = 50;
90const unsigned int default_cache_size = 20;
91const string default_cache_prefix =
"rc";
92const string default_cache_dir =
"";
94const string BESDapFunctionResponseCache::PATH_KEY =
"DAP.FunctionResponseCache.path";
95const string BESDapFunctionResponseCache::PREFIX_KEY =
"DAP.FunctionResponseCache.prefix";
96const string BESDapFunctionResponseCache::SIZE_KEY =
"DAP.FunctionResponseCache.size";
99bool BESDapFunctionResponseCache::d_enabled =
true;
101unsigned long BESDapFunctionResponseCache::get_cache_size_from_config()
105 unsigned long size_in_megabytes = default_cache_size;
109 "BESDapFunctionResponseCache::getCacheSizeFromConfig(): Located BES key " << SIZE_KEY<<
"=" << size << endl);
110 istringstream iss(size);
111 iss >> size_in_megabytes;
114 return size_in_megabytes;
117string BESDapFunctionResponseCache::get_cache_prefix_from_config()
120 string prefix = default_cache_prefix;
124 "BESDapFunctionResponseCache::getCachePrefixFromConfig(): Located BES key " << PREFIX_KEY<<
"=" << prefix << endl);
132string BESDapFunctionResponseCache::get_cache_dir_from_config()
136 string cacheDir = default_cache_dir;
140 "BESDapFunctionResponseCache::getCacheDirFromConfig(): Located BES key " << PATH_KEY<<
"=" << cacheDir << endl);
164BESDapFunctionResponseCache::get_instance(
const string &cache_dir,
const string &prefix,
unsigned long long size)
166 if (d_enabled && d_instance == 0) {
167 if (!cache_dir.empty() &&
dir_exists(cache_dir)) {
168 d_instance =
new BESDapFunctionResponseCache(cache_dir, prefix, size);
169 d_enabled = d_instance->cache_enabled();
173 BESDEBUG(
"cache",
"BESDapFunctionResponseCache::"<<__func__ <<
"() - " <<
174 "Cache is DISABLED"<< endl);
178 atexit(delete_instance);
180 BESDEBUG(
"cache",
"BESDapFunctionResponseCache::"<<__func__ <<
"() - " <<
181 "Cache is ENABLED"<< endl);
187 "BESDapFunctionResponseCache::get_instance(dir,prefix,size) - d_instance: " << d_instance << endl);
193BESDapFunctionResponseCache::get_instance()
195 if (d_enabled && d_instance == 0) {
196 string cache_dir = get_cache_dir_from_config();
197 if (!cache_dir.empty() &&
dir_exists(cache_dir)) {
198 d_instance =
new BESDapFunctionResponseCache(get_cache_dir_from_config(), get_cache_prefix_from_config(),
199 get_cache_size_from_config());
200 d_enabled = d_instance->cache_enabled();
204 BESDEBUG(
"cache",
"BESDapFunctionResponseCache::"<<__func__ <<
"() - " <<
205 "Cache is DISABLED"<< endl);
209 atexit(delete_instance);
211 BESDEBUG(
"cache",
"BESDapFunctionResponseCache::"<<__func__ <<
"() - " <<
212 "Cache is ENABLED"<< endl);
217 BESDEBUG(DEBUG_KEY,
"BESDapFunctionResponseCache::get_instance() - d_instance: " << (
void *) d_instance << endl);
232bool BESDapFunctionResponseCache::is_valid(
const string &cache_file_name,
const string &dataset)
238 off_t entry_size = 0;
239 time_t entry_time = 0;
241 if (stat(cache_file_name.c_str(), &buf) == 0) {
242 entry_size = buf.st_size;
243 entry_time = buf.st_mtime;
249 if (entry_size == 0)
return false;
251 time_t dataset_time = entry_time;
252 if (stat(dataset.c_str(), &buf) == 0) {
253 dataset_time = buf.st_mtime;
260 if (dataset_time > entry_time)
return false;
265string BESDapFunctionResponseCache::get_resource_id(DDS *dds,
const string &constraint)
267 return dds->filename() +
"#" + constraint;
270bool BESDapFunctionResponseCache::can_be_cached(DDS *dds,
const string &constraint)
272 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" constraint + dds->filename() length: "
273 << constraint.size() + dds->filename().size() << endl);
275 return (constraint.size() + dds->filename().size() <= max_cacheable_ce_len);
285string BESDapFunctionResponseCache::get_hash_basename(
const string &resource_id)
288 HASH_OBJ<string> str_hash;
289 size_t hashValue = str_hash(resource_id);
290 stringstream hashed_id;
291 hashed_id << hashValue;
295 return cache_file_name;
324 string resourceId = dds->filename() +
"#" + constraint;
326 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" resourceId: '" << resourceId <<
"'" << endl);
329 HASH_OBJ<string> str_hash;
332 size_t hashValue = str_hash(resourceId);
333 stringstream hashed_id;
334 hashed_id << hashValue;
336 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" hashed_id: '" << hashed_id.str() <<
"'" << endl);
342 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" cache_file_name: '" << cache_file_name <<
"'" << endl);
348 if ((ret_dds = load_from_cache(resourceId, cache_file_name))) {
349 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" Data loaded from cache file: " << cache_file_name << endl);
350 ret_dds->filename(dds->filename());
352 else if ((ret_dds = write_dataset_to_cache(dds, resourceId, constraint, cache_file_name))) {
353 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" Data written to cache file: " << cache_file_name << endl);
357 else if ((ret_dds = load_from_cache(resourceId, cache_file_name))) {
358 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" Data loaded from cache file (2nd try): " << cache_file_name << endl);
359 ret_dds->filename(dds->filename());
362 BESDEBUG(DEBUG_KEY,__FUNCTION__ <<
" Used cache_file_name: " << cache_file_name <<
" for resource ID: " << resourceId << endl);
384BESDapFunctionResponseCache::load_from_cache(
const string &resource_id,
string &cache_file_name)
386 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" resource_id: " << resource_id << endl);
390 unsigned long suffix_counter = 0;
391 bool keep_looking =
true;
393 if (suffix_counter > max_collisions) {
395 ss <<
"Cache error! There are " << suffix_counter <<
" hash collisions for the resource '" << resource_id
396 <<
"' And that is a bad bad thing.";
402 cfname << cache_file_name <<
"_" << suffix_counter++;
404 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" candidate cache_file_name: " << cfname.str() << endl);
408 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" !get_read_lock(cfname.str(), fd): " << fd << endl);
411 keep_looking =
false;
414 cache_file_name = cfname.str();
421 ifstream cache_file_istream(cfname.str().c_str());
422 char line[max_cacheable_ce_len];
423 cache_file_istream.getline(line, max_cacheable_ce_len);
424 string cached_resource_id;
425 cached_resource_id.assign(line);
427 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" cached_resource_id: " << cached_resource_id << endl);
429 if (cached_resource_id.compare(resource_id) == 0) {
431 BESDEBUG(DEBUG_KEY,
"BESDapFunctionResponseCache::load_from_cache() - Cache Hit!" << endl);
434 cached_dds = read_cached_data(cache_file_istream);
439 }
while (!cached_dds && keep_looking);
441 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" Cache " << (cached_dds!=0?
"HIT":
"MISS") <<
" for: " << cache_file_name << endl);
451BESDapFunctionResponseCache::read_cached_data(istream &cached_data)
454 CacheTypeFactory factory;
455 DDS *fdds =
new DDS(&factory);
457 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" - BEGIN" << endl);
460 DDXParser ddx_parser(fdds->get_factory());
466 ddx_parser.intern_stream(cached_data, fdds, data_cid, DATA_MARK);
469 throw BESInternalError(e.get_error_message(), __FILE__, __LINE__);
472 CacheUnMarshaller um(cached_data);
474 for (DDS::Vars_iter i = fdds->var_begin(), e = fdds->var_end(); i != e; ++i) {
475 (*i)->deserialize(um, fdds);
480 for (DDS::Vars_iter i = fdds->var_begin(), e = fdds->var_end(); i != e; ++i) {
481 (*i)->set_read_p(
true);
482 (*i)->set_send_p(
true);
488 if ((*i)->type() == dods_sequence_c) {
489 static_cast<Sequence*
>(*i)->reset_row_number(
true);
493 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" - END." << endl);
495 fdds->set_factory(0);
516BESDapFunctionResponseCache::write_dataset_to_cache(DDS *dds,
const string &resource_id,
const string &func_ce,
517 const string &cache_file_name)
519 BESDEBUG(DEBUG_KEY, __FUNCTION__ <<
" BEGIN " << resource_id <<
": "
520 << func_ce <<
": " << cache_file_name << endl);
528 BESDEBUG(DEBUG_KEY,__FUNCTION__ <<
" Caching " << resource_id <<
", func_ce: " << func_ce << endl);
531 ofstream cache_file_ostream(cache_file_name.c_str(), ios::out|ios::app|ios::binary);
532 if (!cache_file_ostream.is_open())
533 throw BESInternalError(
"Could not open '" + cache_file_name +
"' to write cached response.", __FILE__, __LINE__);
537 cache_file_ostream << resource_id << endl;
540 ConstraintEvaluator func_eval;
541 func_eval.parse_constraint(func_ce, *dds);
542 fdds = func_eval.eval_function_clauses(*dds);
544 fdds->print_xml_writer(cache_file_ostream,
true,
"");
546 cache_file_ostream << DATA_MARK << endl;
552 ConstraintEvaluator new_ce;
553 CacheMarshaller m(cache_file_ostream);
555 for (DDS::Vars_iter i = fdds->var_begin(); i != fdds->var_end(); i++) {
556 if ((*i)->send_p()) {
557 (*i)->serialize(new_ce, *fdds, m,
false);
577 cache_file_ostream.close();
Cache the results from server functions.
virtual libdap::DDS * get_or_cache_dataset(libdap::DDS *dds, const std::string &constraint)
Return a DDS loaded with data that can be serialized back to a client.
virtual void unlock_and_close(const std::string &target)
std::string get_cache_directory() const
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)
std::string get_cache_file_prefix() const
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)
exception thrown if internal error encountered
static std::string lowercase(const std::string &s)
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.