bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
DmrppRequestHandler.cc
1// DmrppRequestHandler.cc
2
3// Copyright (c) 2016 OPeNDAP, Inc. Author: James Gallagher
4// <jgallagher@opendap.org>, Patrick West <pwest@opendap.org>
5// Nathan Potter <npotter@opendap.org>
6//
7// modify it under the terms of the GNU Lesser General Public License
8// as published by the Free Software Foundation; either version 2.1 of
9// the License, or (at your option) any later version.
10//
11// This library is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// Lesser General Public License for more details.
15//
16// License along with this library; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18// 02110-1301 U\ SA
19//
20// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI.
21// 02874-0112.
22
23#include "config.h"
24
25#include <string>
26#include <memory>
27#include <sstream>
28
29#include <curl/curl.h>
30
31#include <libdap/Ancillary.h>
32#include <libdap/DMR.h>
33#include <libdap/D4Group.h>
34#include <libdap/DAS.h>
35
36#include <libdap/InternalErr.h>
37#include <libdap/mime_util.h> // for name_path
38
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"
49
50#include "BESDMRResponse.h"
51
52#include "BESConstraintFuncs.h"
53#include "BESServiceRegistry.h"
54#include "BESUtil.h"
55#include "BESLog.h"
56#include "TheBESKeys.h"
57
58#include "BESDapError.h"
59#include "BESInternalFatalError.h"
60#include "BESSyntaxUserError.h"
61#include "BESDebug.h"
62#include "BESStopWatch.h"
63
64#include "NgapOwnedContainer.h"
65
66#include "DapUtils.h"
67
68#define PUGIXML_NO_XPATH
69#define PUGIXML_HEADER_ONLY
70
71#include <pugixml.hpp>
72
73#include "DmrppNames.h"
74#include "DmrppTypeFactory.h"
75#include "DmrppRequestHandler.h"
76#include "CurlHandlePool.h"
77#include "CredentialsManager.h"
78
79using namespace bes;
80using namespace http;
81using namespace libdap;
82using namespace std;
83
84// These should be set in the Makefile.am. jhrg 11/23/24
85#ifndef MODULE_NAME
86#define MODULE_NAME "dmrpp_module"
87#endif
88
89#ifndef MODULE_VERSION
90#define MODULE_VERSION "unset" // Set this in the Makefile.am
91#endif
92
93#define prolog std::string("DmrppRequestHandler::").append(__func__).append("() - ")
94#define dmrpp_cache "dmrpp:cache"
95
96namespace dmrpp {
97
98unique_ptr<ObjMemCache> DmrppRequestHandler::das_cache{nullptr};
99unique_ptr<ObjMemCache> DmrppRequestHandler::dds_cache{nullptr};
100
101shared_ptr<DMZ> DmrppRequestHandler::dmz{nullptr};
102
103// This is used to maintain a pool of reusable curl handles that enable connection
104// reuse. I tried making this a unique_ptr, but that caused tests to fail because
105// the DmrppRequestHandler dtor did not free this object every time the handler
106// was instantiated. That's not an issue for the BES, but it was/is for unit tests
107// that make and delete many of these objects. jhrg 11/22/24
108CurlHandlePool *DmrppRequestHandler::curl_handle_pool{nullptr};
109
110// These now only affect the DDS and DAS ObjMemCaches; the DMR++
111// is cached in the NGAP module. Once issues with the DMR++ object's
112// copy constructor are solved, the ObjMemCache can be used again.
113// jhrg 9/26/23
114bool DmrppRequestHandler::d_use_object_cache = true;
115unsigned int DmrppRequestHandler::d_object_cache_entries = 100;
116double DmrppRequestHandler::d_object_cache_purge_level = 0.2;
117
118bool DmrppRequestHandler::d_use_transfer_threads = true;
119unsigned int DmrppRequestHandler::d_max_transfer_threads = 8;
120
121bool DmrppRequestHandler::d_use_compute_threads = true;
122unsigned int DmrppRequestHandler::d_max_compute_threads = 8;
123
124// Default minimum value is 2MB: 2 * (1024*1024)
125unsigned long long DmrppRequestHandler::d_contiguous_concurrent_threshold = DMRPP_DEFAULT_CONTIGUOUS_CONCURRENT_THRESHOLD;
126
127// This behavior mirrors the SAX2 parser behavior where the software doesn't require that
128// a variable actually have chunks (that is, some variable might not have any data).
129// We could make this a run-time option if needed. jhrg 11/4/21
130bool DmrppRequestHandler::d_require_chunks = false;
131
132// See the comment in the header for more about this kludge. jhrg 11/9/21
133bool DmrppRequestHandler::d_emulate_original_filter_order_behavior = false;
134
135bool DmrppRequestHandler::is_netcdf4_enhanced_response = false;
136bool DmrppRequestHandler::is_netcdf4_classic_response = false;
137
138// The direct IO feature is turned on by default.
139bool DmrppRequestHandler::disable_direct_io = false;
140
141// There are methods in TheBESKeys that should be used instead of these.
142// jhrg 9/26/23
143static void read_key_value(const std::string &key_name, bool &key_value) {
144 bool key_found = false;
145 string value;
146 TheBESKeys::TheKeys()->get_value(key_name, value, key_found);
147 if (key_found) {
148 value = BESUtil::lowercase(value);
149 key_value = (value == "true" || value == "yes");
150 }
151}
152
153static void read_key_value(const std::string &key_name, unsigned int &key_value) {
154 bool key_found = false;
155 string value;
156 TheBESKeys::TheKeys()->get_value(key_name, value, key_found);
157 if (key_found) {
158 istringstream iss(value);
159 iss >> key_value;
160 }
161}
162
163static void read_key_value(const std::string &key_name, unsigned long long &key_value) {
164 bool key_found = false;
165 string value;
166 TheBESKeys::TheKeys()->get_value(key_name, value, key_found);
167 if (key_found) {
168 istringstream iss(value);
169 iss >> key_value;
170 }
171}
172
173static void read_key_value(const std::string &key_name, double &key_value) {
174 bool key_found = false;
175 string value;
176 TheBESKeys::TheKeys()->get_value(key_name, value, key_found);
177 if (key_found) {
178 istringstream iss(value);
179 iss >> key_value;
180 }
181}
182
188 BESRequestHandler(name)
189{
190 add_method(DMR_RESPONSE, dap_build_dmr);
191 add_method(DAP4DATA_RESPONSE, dap_build_dap4data);
192 add_method(DAS_RESPONSE, dap_build_das);
193 add_method(DDS_RESPONSE, dap_build_dds);
194 add_method(DATA_RESPONSE, dap_build_dap2data);
195
196 add_method(VERS_RESPONSE, dap_build_vers);
197 add_method(HELP_RESPONSE, dap_build_help);
198
199 stringstream msg;
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;
205 }
206 else {
207 msg << "Disabled." << endl;
208 }
209
210 INFO_LOG(msg.str());
211 msg.str(std::string());
212
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;
218 }
219 else {
220 msg << "Disabled." << endl;
221 }
222
223 INFO_LOG(msg.str());
224 msg.str(std::string());
225
226 // DMRPP_CONTIGUOUS_CONCURRENT_THRESHOLD_KEY
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;
229 INFO_LOG(msg.str());
230
231 // Whether the default direct IO feature is disabled. Read the key in.
232 read_key_value(DMRPP_DISABLE_DIRECT_IO, disable_direct_io);
233
234 // Check the value of FONc.ClassicModel to determine if this response is a netCDF-4 classic from fileout netCDF
235 // This must be done here since direct IO flag for individual variables should NOT be set for netCDF-4 classic response.
236 read_key_value(DMRPP_USE_CLASSIC_IN_FILEOUT_NETCDF, is_netcdf4_classic_response);
237
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");
241#endif
242
243 if (!curl_handle_pool) {
244 curl_handle_pool = new CurlHandlePool();
245 curl_handle_pool->initialize();
246 }
247
248 // This can be set to true using the bes conf file; the default value is false
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);
253 // The default value of these is nullptr
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);
256 }
257
258 // This and the matching cleanup function can be called many times as long as
259 // they are called in balanced pairs. jhrg 9/3/20
260 // TODO 10/8/21 move this into the http at the top level of the BES. That is, all
261 // calls to this should be moved out of handlers and handlers can/should
262 // assume that curl is present and init'd. jhrg
263 curl_global_init(CURL_GLOBAL_DEFAULT);
264}
265
266DmrppRequestHandler::~DmrppRequestHandler() {
267 delete curl_handle_pool;
268 // generally, this is not necessary, but for this to be used in the unit tests, where the DmrppRequestHandler
269 // is made and destroyed many times, it is necessary. That is because the curl handle pool is a static pointer.
270 // jhrg 11/22/24
271 curl_handle_pool = nullptr;
272 curl_global_cleanup();
273}
274
280void
281handle_exception(const string &file, int line)
282try {
283 throw;
284}
285catch (const BESError &e) {
286 throw;
287}
288catch (const InternalErr &e) {
289 throw BESDapError(e.get_error_message(), true, e.get_error_code(), file, line);
290}
291catch (const Error &e) {
292 throw BESDapError(e.get_error_message(), false, e.get_error_code(), file, line);
293}
294catch (const std::exception &e) {
295 throw BESInternalFatalError(string("C++ exception: ").append(e.what()), file, line);
296}
297catch (...) {
298 throw BESInternalFatalError("Unknown exception caught building DAP4 Data response", file, line);
299}
300
321void DmrppRequestHandler::get_dmrpp_from_container_or_cache(BESContainer *container, DMR *dmr) {
322 try {
323 // If the container is an NGAP container (or maybe other types in the future),
324 // the DMR++ itself might be returned as a string by access(). If the container
325 // did not come from the NGAP handler, the return value might be a string that
326 // names a file on the local host. jhrg 10/19/23
327 string container_attributes = container->get_attributes();
328 if (container_attributes == "as-string") {
329 dmr->set_filename(container_attributes);
330 dmr->set_name(name_path(container_attributes));
331
332 // this shared_ptr is held by the DMRpp BaseType instances
333 dmz = make_shared<DMZ>();
334
335 // Enable adding the DMZ to the BaseTypes built by the factory
336 DmrppTypeFactory factory(dmz);
337 dmr->set_factory(&factory);
338
339 string dmrpp_content = container->access();
340
341 dmz->parse_xml_string(dmrpp_content);
342
343 dmz->build_thin_dmr(dmr);
344
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);
353 }
354 }
355
356 dmz->load_all_attributes(dmr);
357 }
358 else {
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);
363
364 // this shared_ptr is held by the DMRpp BaseType instances
365 dmz = make_shared<DMZ>();
366
367 // Enable adding the DMZ to the BaseTypes built by the factory
368 DmrppTypeFactory factory(dmz);
369 dmr->set_factory(&factory);
370
371 dmz->parse_xml_doc(data_pathname);
372
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);
382 }
383 }
384
385 dmz->load_all_attributes(dmr);
386 }
387 }
388 catch (...) {
389 handle_exception(__FILE__, __LINE__);
390 }
391}
392
402template<class T>
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() : "";
405
406 // Since this must be a BESDDSResponse or BESDataDDSResponse, we know that a DDS
407 // can be accessed.
408 DDS *dds = bdds->get_dds();
409 if (!container_name_str.empty()) dds->container_name(container_name_str);
410
411 // Inserted new code here
412 string filename = container->get_real_name();
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);
416 // copy the cached DMR into the BES response object
417 *dds = *cached_dds; // Copy the cached object
418 }
419 else {
420 BESDEBUG(dmrpp_cache, prolog << "DDS Cache miss for : " << filename << endl);
421 DMR dmr;
422 get_dmrpp_from_container_or_cache(container, &dmr);
423
424 delete dds; // delete the current one;
425 dds = dmr.getDDS(); // assign the new one.
426
427 // Stuff it into the response so that the BESDDSResponse instances manages the storage.
428 bdds->set_dds(dds);
429
430 // Cache it, if the cache is active.
431 if (dds_cache) {
432 dds_cache->add(new DDS(*dds), filename);
433 }
434 }
435}
436
450 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
451
452 auto bdmr = dynamic_cast<BESDMRResponse *>(dhi.response_handler->get_response_object());
453 if (!bdmr) throw BESInternalError("Cast error, expected a BESDMRResponse object.", __FILE__, __LINE__);
454
455 try {
456 get_dmrpp_from_container_or_cache(dhi.container, bdmr->get_dmr());
457
458 bdmr->set_dap4_constraint(dhi);
459 bdmr->set_dap4_function(dhi);
460 }
461 catch (...) {
462 handle_exception(__FILE__, __LINE__);
463 }
464
465 BESDEBUG(MODULE, prolog << "END" << endl);
466 return true;
467}
468
475 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
476
477 auto bdmr = dynamic_cast<BESDMRResponse *>(dhi.response_handler->get_response_object());
478 if (!bdmr) throw BESInternalError("Cast error, expected a BESDMRResponse object.", __FILE__, __LINE__);
479
480 try {
481 bool is_netcdf4_response = (dhi.data["return_command"] == "netcdf-4");
482
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;
487
488 BESDEBUG(MODULE,
489 prolog << "netcdf4_enhanced_response: " << DmrppRequestHandler::is_netcdf4_enhanced_response << endl);
490
491 BESDEBUG(MODULE, prolog << "netcdf4_classic_response: "
492 << (is_netcdf4_response && DmrppRequestHandler::is_netcdf4_classic_response) << endl);
493
494 get_dmrpp_from_container_or_cache(dhi.container, bdmr->get_dmr());
495
496 bdmr->set_dap4_constraint(dhi);
497 bdmr->set_dap4_function(dhi);
498 }
499 catch (...) {
500 handle_exception(__FILE__, __LINE__);
501 }
502
503 BESDEBUG(MODULE, prolog << "END" << endl);
504 return true;
505}
506
507
512 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
513
514 auto bdds = dynamic_cast<BESDataDDSResponse *>(dhi.response_handler->get_response_object());
515 if (!bdds) throw BESInternalError("Cast error, expected a BESDataDDSResponse object.", __FILE__, __LINE__);
516
517 try {
518 get_dds_from_dmr_or_cache<BESDataDDSResponse>(dhi.container, bdds);
519
520 bdds->set_constraint(dhi);
521 bdds->clear_container();
522 }
523 catch (...) {
524 handle_exception(__FILE__, __LINE__);
525 }
526
527 BESDEBUG(MODULE, prolog << "END" << endl);
528 return true;
529}
530
531
536 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
537
538 auto bdds = dynamic_cast<BESDDSResponse *>(dhi.response_handler->get_response_object());
539 if (!bdds) throw BESInternalError("Cast error, expected a BESDDSResponse object.", __FILE__, __LINE__);
540
541 try {
542 get_dds_from_dmr_or_cache<BESDDSResponse>(dhi.container, bdds);
543
544 bdds->set_constraint(dhi);
545 bdds->clear_container();
546 }
547 catch (...) {
548 handle_exception(__FILE__, __LINE__);
549 }
550
551 BESDEBUG(MODULE, prolog << "END" << endl);
552 return true;
553}
554
560 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
561
562 auto bdas = dynamic_cast<BESDASResponse *>(dhi.response_handler->get_response_object());
563 if (!bdas) throw BESInternalError("Cast error, expected a BESDASResponse object.", __FILE__, __LINE__);
564
565 try {
566 string container_name = bdas->get_explicit_containers() ? dhi.container->get_symbolic_name() : "";
567
568 DAS *das = bdas->get_das();
569 if (!container_name.empty()) das->container_name(container_name);
570
571 string filename = dhi.container->get_real_name();
572 // Look in memory cache (if it's initialized)
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);
576 // copy the cached DAS into the BES response object
577 *das = *cached_das;
578 }
579 else {
580 BESDEBUG(dmrpp_cache, prolog << "DAS Cache miss for : " << filename << endl);
581 DMR dmr;
582 get_dmrpp_from_container_or_cache(dhi.container, &dmr);
583
584 // Get a DDS from the DMR, getDDS() allocates all new objects. Use unique_ptr
585 // to ensure this is deleted. jhrg 11/12/21
586 unique_ptr<DDS> dds(dmr.getDDS());
587
588 dds->mark_all(true);
589 dap_utils::throw_for_dap4_typed_vars_or_attrs(dds.get(), __FILE__, __LINE__);
590
591 // Load the BESDASResponse DAS from the DDS
592 dds->get_das(das);
593
594 // I'm not sure that this makes much sense, but there could be a local DMR++ and
595 // it could have ancillary das info.
596 Ancillary::read_ancillary_das(*das, filename);
597
598 // Add to cache if cache is active
599 if (das_cache) {
600 das_cache->add(new DAS(*das), filename);
601 }
602 }
603
604 bdas->clear_container();
605 }
606 catch (...) {
607 handle_exception(__FILE__, __LINE__);
608 }
609
610 BESDEBUG(MODULE, prolog << "END" << endl);
611 return true;
612}
613
614
615bool DmrppRequestHandler::dap_build_vers(BESDataHandlerInterface &dhi) {
616 auto info = dynamic_cast<BESVersionInfo *>(dhi.response_handler->get_response_object());
617 if (!info) throw BESInternalFatalError("Expected a BESVersionInfo instance.", __FILE__, __LINE__);
618
619 info->add_module(MODULE_NAME, MODULE_VERSION);
620 return true;
621}
622
623bool DmrppRequestHandler::dap_build_help(BESDataHandlerInterface &dhi) {
624 auto info = dynamic_cast<BESInfo *>(dhi.response_handler->get_response_object());
625 if (!info) throw BESInternalFatalError("Expected a BESVersionInfo instance.", __FILE__, __LINE__);
626
627 // This is an example. If you had a help file you could load it like
628 // this and if your handler handled the following responses.
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()) {
635 string handles = BESUtil::implode(services, ',');
636 attrs["handles"] = handles;
637 }
638 info->begin_tag("module", &attrs);
639 info->end_tag("module");
640
641 return true;
642}
643
644void DmrppRequestHandler::dump(ostream &strm) const {
645 strm << BESIndent::LMarg << "DmrppRequestHandler::dump - (" << (void *) this << ")" << endl;
646 BESIndent::Indent();
648 BESIndent::UnIndent();
649}
650
651} // namespace dmrpp
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
Definition BESInfo.h:63
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)
Definition BESUtil.cc:257
static std::string implode(const std::list< std::string > &values, char delim)
Definition BESUtil.cc:620
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.
Definition TheBESKeys.cc:85
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
Definition TheBESKeys.h:51