31#include <libdap/Ancillary.h>
32#include <libdap/DMR.h>
33#include <libdap/D4Group.h>
34#include <libdap/DAS.h>
36#include <libdap/InternalErr.h>
37#include <libdap/mime_util.h>
39#include "BESResponseHandler.h"
40#include "BESResponseNames.h"
41#include "BESDapNames.h"
42#include "BESDataNames.h"
43#include "BESDASResponse.h"
44#include "BESDDSResponse.h"
45#include "BESDataDDSResponse.h"
46#include "BESVersionInfo.h"
47#include "BESContainer.h"
48#include "ObjMemCache.h"
50#include "BESDMRResponse.h"
52#include "BESConstraintFuncs.h"
53#include "BESServiceRegistry.h"
56#include "TheBESKeys.h"
58#include "BESDapError.h"
59#include "BESInternalFatalError.h"
60#include "BESSyntaxUserError.h"
62#include "BESStopWatch.h"
64#include "NgapOwnedContainer.h"
68#define PUGIXML_NO_XPATH
69#define PUGIXML_HEADER_ONLY
73#include "DmrppNames.h"
74#include "DmrppTypeFactory.h"
75#include "DmrppRequestHandler.h"
76#include "CurlHandlePool.h"
77#include "CredentialsManager.h"
86#define MODULE_NAME "dmrpp_module"
90#define MODULE_VERSION "unset"
93#define prolog std::string("DmrppRequestHandler::").append(__func__).append("() - ")
94#define dmrpp_cache "dmrpp:cache"
98unique_ptr<ObjMemCache> DmrppRequestHandler::das_cache{
nullptr};
99unique_ptr<ObjMemCache> DmrppRequestHandler::dds_cache{
nullptr};
101shared_ptr<DMZ> DmrppRequestHandler::dmz{
nullptr};
114bool DmrppRequestHandler::d_use_object_cache =
true;
115unsigned int DmrppRequestHandler::d_object_cache_entries = 100;
116double DmrppRequestHandler::d_object_cache_purge_level = 0.2;
118bool DmrppRequestHandler::d_use_transfer_threads =
true;
119unsigned int DmrppRequestHandler::d_max_transfer_threads = 8;
121bool DmrppRequestHandler::d_use_compute_threads =
true;
122unsigned int DmrppRequestHandler::d_max_compute_threads = 8;
125unsigned long long DmrppRequestHandler::d_contiguous_concurrent_threshold = DMRPP_DEFAULT_CONTIGUOUS_CONCURRENT_THRESHOLD;
130bool DmrppRequestHandler::d_require_chunks =
false;
133bool DmrppRequestHandler::d_emulate_original_filter_order_behavior =
false;
135bool DmrppRequestHandler::is_netcdf4_enhanced_response =
false;
136bool DmrppRequestHandler::is_netcdf4_classic_response =
false;
139bool DmrppRequestHandler::disable_direct_io =
false;
143static void read_key_value(
const std::string &key_name,
bool &key_value) {
144 bool key_found =
false;
149 key_value = (value ==
"true" || value ==
"yes");
153static void read_key_value(
const std::string &key_name,
unsigned int &key_value) {
154 bool key_found =
false;
158 istringstream iss(value);
163static void read_key_value(
const std::string &key_name,
unsigned long long &key_value) {
164 bool key_found =
false;
168 istringstream iss(value);
173static void read_key_value(
const std::string &key_name,
double &key_value) {
174 bool key_found =
false;
178 istringstream iss(value);
188 BESRequestHandler(name)
200 read_key_value(DMRPP_USE_TRANSFER_THREADS_KEY, d_use_transfer_threads);
201 read_key_value(DMRPP_MAX_TRANSFER_THREADS_KEY, d_max_transfer_threads);
202 msg << prolog <<
"Concurrent Transfer Threads: ";
203 if (DmrppRequestHandler::d_use_transfer_threads) {
204 msg <<
"Enabled. max_transfer_threads: " << DmrppRequestHandler::d_max_transfer_threads << endl;
207 msg <<
"Disabled." << endl;
211 msg.str(std::string());
213 read_key_value(DMRPP_USE_COMPUTE_THREADS_KEY, d_use_compute_threads);
214 read_key_value(DMRPP_MAX_COMPUTE_THREADS_KEY, d_max_compute_threads);
215 msg << prolog <<
"Concurrent Compute Threads: ";
216 if (DmrppRequestHandler::d_use_compute_threads) {
217 msg <<
"Enabled. max_compute_threads: " << DmrppRequestHandler::d_max_compute_threads << endl;
220 msg <<
"Disabled." << endl;
224 msg.str(std::string());
227 read_key_value(DMRPP_CONTIGUOUS_CONCURRENT_THRESHOLD_KEY, d_contiguous_concurrent_threshold);
228 msg << prolog <<
"Contiguous Concurrency Threshold: " << d_contiguous_concurrent_threshold <<
" bytes." << endl;
232 read_key_value(DMRPP_DISABLE_DIRECT_IO, disable_direct_io);
236 read_key_value(DMRPP_USE_CLASSIC_IN_FILEOUT_NETCDF, is_netcdf4_classic_response);
238#if !HAVE_CURL_MULTI_API
239 if (DmrppRequestHandler::d_use_transfer_threads)
240 ERROR_LOG(
"The DMR++ handler is configured to use parallel transfers, but the libcurl Multi API is not present, defaulting to serial transfers");
243 if (!curl_handle_pool) {
245 curl_handle_pool->initialize();
249 read_key_value(DMRPP_USE_OBJECT_CACHE_KEY, d_use_object_cache);
250 if (d_use_object_cache) {
251 read_key_value(DMRPP_OBJECT_CACHE_ENTRIES_KEY, d_object_cache_entries);
252 read_key_value(DMRPP_OBJECT_CACHE_PURGE_LEVEL_KEY, d_object_cache_purge_level);
254 dds_cache = make_unique<ObjMemCache>(d_object_cache_entries, d_object_cache_purge_level);
255 das_cache = make_unique<ObjMemCache>(d_object_cache_entries, d_object_cache_purge_level);
263 curl_global_init(CURL_GLOBAL_DEFAULT);
266DmrppRequestHandler::~DmrppRequestHandler() {
267 delete curl_handle_pool;
271 curl_handle_pool =
nullptr;
272 curl_global_cleanup();
281handle_exception(
const string &file,
int line)
285catch (
const BESError &e) {
288catch (
const InternalErr &e) {
289 throw BESDapError(e.get_error_message(),
true, e.get_error_code(), file, line);
291catch (
const Error &e) {
292 throw BESDapError(e.get_error_message(),
false, e.get_error_code(), file, line);
294catch (
const std::exception &e) {
295 throw BESInternalFatalError(
string(
"C++ exception: ").append(e.what()), file, line);
298 throw BESInternalFatalError(
"Unknown exception caught building DAP4 Data response", file, line);
321void DmrppRequestHandler::get_dmrpp_from_container_or_cache(BESContainer *container, DMR *dmr) {
328 if (container_attributes ==
"as-string") {
329 dmr->set_filename(container_attributes);
330 dmr->set_name(name_path(container_attributes));
333 dmz = make_shared<DMZ>();
336 DmrppTypeFactory factory(dmz);
337 dmr->set_factory(&factory);
339 string dmrpp_content = container->
access();
341 dmz->parse_xml_string(dmrpp_content);
343 dmz->build_thin_dmr(dmr);
345 BESDEBUG(
"dmrpp",
"Before calling set_up_all_direct_io_flags"<<endl);
346 if (DmrppRequestHandler::is_netcdf4_enhanced_response ==
true &&
347 DmrppRequestHandler::disable_direct_io ==
false) {
348 BESDEBUG(
"dmrpp",
"calling set_up_all_direct_io_flags"<<endl);
349 bool global_dio_flag = dmz->set_up_all_direct_io_flags_phase_1(dmr);
350 if (global_dio_flag) {
351 BESDEBUG(
"dmrpp",
"global_dio_flags is true."<<endl);
352 dmz->set_up_all_direct_io_flags_phase_2(dmr);
356 dmz->load_all_attributes(dmr);
359 string data_pathname = container->
access();
360 dmr->set_filename(data_pathname);
361 dmr->set_name(name_path(data_pathname));
362 BESDEBUG(dmrpp_cache, prolog <<
"DMR Cache miss for : " << container->
get_real_name() << endl);
365 dmz = make_shared<DMZ>();
368 DmrppTypeFactory factory(dmz);
369 dmr->set_factory(&factory);
371 dmz->parse_xml_doc(data_pathname);
373 dmz->build_thin_dmr(dmr);
374 BESDEBUG(
"dmrpp",
"Before calling set_up_all_direct_io_flags: second"<<endl);
375 if (DmrppRequestHandler::is_netcdf4_enhanced_response ==
true &&
376 DmrppRequestHandler::disable_direct_io ==
false) {
377 BESDEBUG(
"dmrpp",
"calling set_up_all_direct_io_flags: second"<<endl);
378 bool global_dio_flag = dmz->set_up_all_direct_io_flags_phase_1(dmr);
379 if (global_dio_flag) {
380 BESDEBUG(
"dmrpp",
"global_dio_flags is true."<<endl);
381 dmz->set_up_all_direct_io_flags_phase_2(dmr);
385 dmz->load_all_attributes(dmr);
389 handle_exception(__FILE__, __LINE__);
403void DmrppRequestHandler::get_dds_from_dmr_or_cache(BESContainer *container, T *bdds) {
404 string container_name_str = bdds->get_explicit_containers() ? container->
get_symbolic_name() :
"";
408 DDS *dds = bdds->get_dds();
409 if (!container_name_str.empty()) dds->container_name(container_name_str);
413 const DDS *cached_dds =
nullptr;
414 if (dds_cache && (cached_dds =
dynamic_cast<DDS *
>(dds_cache->get(filename)))) {
415 BESDEBUG(dmrpp_cache, prolog <<
"DDS Cache hit for : " << filename << endl);
420 BESDEBUG(dmrpp_cache, prolog <<
"DDS Cache miss for : " << filename << endl);
422 get_dmrpp_from_container_or_cache(container, &dmr);
432 dds_cache->add(
new DDS(*dds), filename);
450 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
453 if (!bdmr)
throw BESInternalError(
"Cast error, expected a BESDMRResponse object.", __FILE__, __LINE__);
456 get_dmrpp_from_container_or_cache(dhi.
container, bdmr->get_dmr());
458 bdmr->set_dap4_constraint(dhi);
459 bdmr->set_dap4_function(dhi);
462 handle_exception(__FILE__, __LINE__);
465 BESDEBUG(MODULE, prolog <<
"END" << endl);
475 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
478 if (!bdmr)
throw BESInternalError(
"Cast error, expected a BESDMRResponse object.", __FILE__, __LINE__);
481 bool is_netcdf4_response = (dhi.
data[
"return_command"] ==
"netcdf-4");
483 DmrppRequestHandler::is_netcdf4_enhanced_response = is_netcdf4_response;
484 if (DmrppRequestHandler::is_netcdf4_enhanced_response &&
485 DmrppRequestHandler::is_netcdf4_classic_response)
486 DmrppRequestHandler::is_netcdf4_enhanced_response =
false;
489 prolog <<
"netcdf4_enhanced_response: " << DmrppRequestHandler::is_netcdf4_enhanced_response << endl);
491 BESDEBUG(MODULE, prolog <<
"netcdf4_classic_response: "
492 << (is_netcdf4_response && DmrppRequestHandler::is_netcdf4_classic_response) << endl);
494 get_dmrpp_from_container_or_cache(dhi.
container, bdmr->get_dmr());
496 bdmr->set_dap4_constraint(dhi);
497 bdmr->set_dap4_function(dhi);
500 handle_exception(__FILE__, __LINE__);
503 BESDEBUG(MODULE, prolog <<
"END" << endl);
512 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
515 if (!bdds)
throw BESInternalError(
"Cast error, expected a BESDataDDSResponse object.", __FILE__, __LINE__);
518 get_dds_from_dmr_or_cache<BESDataDDSResponse>(dhi.
container, bdds);
520 bdds->set_constraint(dhi);
521 bdds->clear_container();
524 handle_exception(__FILE__, __LINE__);
527 BESDEBUG(MODULE, prolog <<
"END" << endl);
536 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
539 if (!bdds)
throw BESInternalError(
"Cast error, expected a BESDDSResponse object.", __FILE__, __LINE__);
542 get_dds_from_dmr_or_cache<BESDDSResponse>(dhi.
container, bdds);
544 bdds->set_constraint(dhi);
545 bdds->clear_container();
548 handle_exception(__FILE__, __LINE__);
551 BESDEBUG(MODULE, prolog <<
"END" << endl);
560 BESDEBUG(MODULE, prolog <<
"BEGIN" << endl);
563 if (!bdas)
throw BESInternalError(
"Cast error, expected a BESDASResponse object.", __FILE__, __LINE__);
568 DAS *das = bdas->get_das();
569 if (!container_name.empty()) das->container_name(container_name);
573 const DAS *cached_das =
nullptr;
574 if (das_cache && (cached_das =
static_cast<DAS *
>(das_cache->get(filename)))) {
575 BESDEBUG(dmrpp_cache, prolog <<
"DAS Cache hit for : " << filename << endl);
580 BESDEBUG(dmrpp_cache, prolog <<
"DAS Cache miss for : " << filename << endl);
582 get_dmrpp_from_container_or_cache(dhi.
container, &dmr);
586 unique_ptr<DDS> dds(dmr.getDDS());
589 dap_utils::throw_for_dap4_typed_vars_or_attrs(dds.get(), __FILE__, __LINE__);
596 Ancillary::read_ancillary_das(*das, filename);
600 das_cache->add(
new DAS(*das), filename);
604 bdas->clear_container();
607 handle_exception(__FILE__, __LINE__);
610 BESDEBUG(MODULE, prolog <<
"END" << endl);
619 info->add_module(MODULE_NAME, MODULE_VERSION);
629 map<string, string, std::less<>> attrs;
630 attrs[
"name"] = MODULE_NAME;
631 attrs[
"version"] = MODULE_VERSION;
632 list<string> services;
633 BESServiceRegistry::TheRegistry()->services_handled(MODULE, services);
634 if (!services.empty()) {
636 attrs[
"handles"] = handles;
638 info->begin_tag(
"module", &attrs);
639 info->end_tag(
"module");
645 strm << BESIndent::LMarg <<
"DmrppRequestHandler::dump - (" << (
void *)
this <<
")" << endl;
648 BESIndent::UnIndent();
std::string get_attributes() const
retrieve the attributes desired from this container
std::string get_symbolic_name() const
retrieve the symbolic name for this container
virtual std::string access()=0
returns the true name of this container
std::string get_real_name() const
retrieve the real name for this container, such as a file name.
Represents an OPeNDAP DAS DAP2 data object within the BES.
Holds a DDS object within the BES.
Represents an OPeNDAP DMR DAP4 data object within the BES.
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
Structure storing information used by the BES to handle the request.
std::map< std::string, std::string > data
the map of string data that will be required for the current request.
BESContainer * container
pointer to current container in this interface
informational response object
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
virtual bool add_method(const std::string &name, p_request_handler_method method)
add a handler method to the request handler that knows how to fill in a specific response object
virtual void dump(std::ostream &strm) const
dumps information about this object
virtual BESResponseObject * get_response_object()
return the current response object
static std::string lowercase(const std::string &s)
static std::string implode(const std::list< std::string > &values, char delim)
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.
static bool dap_build_dds(BESDataHandlerInterface &dhi)
void dump(std::ostream &strm) const override
dumps information about this object
static bool dap_build_dap2data(BESDataHandlerInterface &dhi)
static bool dap_build_dmr(BESDataHandlerInterface &dhi)
static bool dap_build_das(BESDataHandlerInterface &dhi)
static bool dap_build_dap4data(BESDataHandlerInterface &dhi)
Build a DAP4 data response. Adds timing to dap_build_dmr()
DmrppRequestHandler(const std::string &name)
utility class for the HTTP catalog module