bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
DapRequestHandler.cc
1// DapRequestHandler.cc
2
3// Copyright (c) 2013 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#include "config.h"
23
24#include <string>
25#include <memory>
26
27#include "DapRequestHandler.h"
28
29#include <BESResponseHandler.h>
30#include <BESResponseNames.h>
31#include <BESVersionInfo.h>
32#include <BESTextInfo.h>
33#include <BESDapNames.h>
34
35#include <BESDataDDSResponse.h>
36#include <BESDDSResponse.h>
37#include <BESDASResponse.h>
38#include <BESDMRResponse.h>
39
40#include <BESConstraintFuncs.h>
41#include <BESServiceRegistry.h>
42#include <BESUtil.h>
43#include <TheBESKeys.h>
44
45#include <BESDapError.h>
46#include <BESInternalFatalError.h>
47#include <BESDebug.h>
48
49#include <libdap/BaseTypeFactory.h>
50#include <test/TestTypeFactory.h>
51#include <libdap/D4BaseTypeFactory.h>
52#include <test/D4TestTypeFactory.h>
53#include <test/TestCommon.h>
54
55#include <libdap/DMR.h>
56#include <libdap/D4Group.h>
57#include <libdap/D4Connect.h>
58#include <libdap/D4ParserSax2.h>
59
60#include <libdap/Ancillary.h>
61#include <libdap/Connect.h>
62#include <libdap/Response.h>
63#include <libdap/InternalErr.h>
64#include <libdap/mime_util.h>
65
66using namespace libdap;
67
68int test_variable_sleep_interval = 0;
69
70bool DapRequestHandler::d_use_series_values = true;
71bool DapRequestHandler::d_use_series_values_set = false;
72
73bool DapRequestHandler::d_use_test_types = true;
74bool DapRequestHandler::d_use_test_types_set = false;
75
76const string module = "dapreader";
77
78static void read_key_value(const std::string &key_name, bool &key_value, bool &is_key_set)
79{
80 if (is_key_set == false) {
81 bool key_found = false;
82 string doset;
83 TheBESKeys::TheKeys()->get_value(key_name, doset, key_found);
84 if (key_found) {
85 // It was set in the conf file
86 is_key_set = true;
87
88 doset = BESUtil::lowercase(doset);
89 key_value = (doset == "true" || doset == "yes");
90 }
91 }
92}
93
94static bool extension_match(const string &data_source, const string &extension)
95{
96 string::size_type pos = data_source.rfind(extension);
97 return pos != string::npos && pos + extension.size() == data_source.size();
98}
99
100DapRequestHandler::DapRequestHandler(const string &name) :
102{
103 add_method(DAS_RESPONSE, dap_build_das);
104 add_method(DDS_RESPONSE, dap_build_dds);
105 add_method(DATA_RESPONSE, dap_build_data);
106
107 add_method(DMR_RESPONSE, dap_build_dmr);
108 add_method(DAP4DATA_RESPONSE, dap_build_dap4data);
109
110 add_method(VERS_RESPONSE, dap_build_vers);
111 add_method(HELP_RESPONSE, dap_build_help);
112
113 read_key_value("DR.UseTestTypes", d_use_test_types, d_use_test_types_set);
114 read_key_value("DR.UseSeriesValues", d_use_series_values, d_use_series_values_set);
115}
116
123void DapRequestHandler::load_dds_from_data_file(const string &accessed, DDS &dds)
124{
125 BESDEBUG("dapreader", "In DapRequestHandler::load_dds_from_data_file; accessed: " << accessed << endl);
126
127 TestTypeFactory t_factory;
128 BaseTypeFactory b_factory;
129 if (d_use_test_types)
130 dds.set_factory(&t_factory);
131 //valgrind shows the leaking caused by the following line. KY 2019-12-12
132 //dds.set_factory(new TestTypeFactory); // DDS deletes the factory
133 else
134 dds.set_factory(&b_factory);
135 //valgrind shows the leaking caused by the following line. KY 2019-12-12
136 //dds.set_factory(new BaseTypeFactory);
137
138 unique_ptr<Connect> url(new Connect(accessed));
139 Response r(fopen(accessed.c_str(), "r"), 0);
140 if (!r.get_stream()) throw Error(string("The input source: ") + accessed + string(" could not be opened"));
141 url->read_data_no_mime(dds, &r);
142
143 unique_ptr<DAS> das(new DAS);
144 Ancillary::read_ancillary_das(*das, accessed);
145
146 if (das->get_size() > 0) dds.transfer_attributes(das.get());
147
148 // This is needed for the values read to show up. Without it the default
149 // behavior of the TestTypes will take over and the values from the data files
150 // will be ignored.
151 for (auto i = dds.var_begin(), e = dds.var_end(); i != e; i++) {
152 (*i)->set_read_p(true);
153 }
154}
155
163void DapRequestHandler::build_dds_from_file(const string &accessed, bool explicit_containers, DDS *dds)
164{
165 BESDEBUG("dapreader", "In DapRequestHandler::build_dds_from_file; accessed: " << accessed << endl);
166
167 if (extension_match(accessed, ".dds") && d_use_test_types) {
168 dds->set_factory(new TestTypeFactory);
169 dds->parse(accessed); // This sets the dataset name based on what's in the file
170
171 DAS *das = new DAS;
172 Ancillary::read_ancillary_das(*das, accessed);
173
174 if (das->get_size() > 0) dds->transfer_attributes(das);
175 }
176 else if (extension_match(accessed, ".dods") || extension_match(accessed, ".data")) {
177 if (explicit_containers) {
178 BESDEBUG("dapreader", "In DapRequestHandler::build_dds_from_file; in container code" << endl);
179 DDS local_dds(0);
180
181 // This function reads from a .dods, ..., 'frozen response' and loads
182 // the values into a DDS's variables. It then merges the Attributes read
183 // from a matching .das file into those variables. The code in Connect
184 // that reads the values is not 'container safe' so we use this function
185 // to read value into a 'local dds' and then transfer its variables to
186 // the real BESDDSResponseObject, which is the DDS passed to this function
187 load_dds_from_data_file(accessed, local_dds);
188
189 // Transfer variables just read into BESDDSResponse/BESDataDDSResponse's DDS
190 for (DDS::Vars_iter i = local_dds.var_begin(), e = local_dds.var_end(); i != e; i++) {
191 dds->add_var((*i)); // copy the variables; figure out how to not copy them
192 }
193
194 dds->set_dataset_name(name_path(accessed));
195 }
196 else {
197 BESDEBUG("dapreader", "In DapRequestHandler::build_dds_from_file; in plain code" << endl);
198 // In the non-container case, reading the values is pretty straightforward
199 load_dds_from_data_file(accessed, *dds);
200 }
201
202 dds->filename(accessed);
203 }
204 else {
205 throw Error("The dapreader module can only return DDS/DODS responses for files ending in .dods, .data or .dds");
206 }
207
208 BESDEBUG("dapreader2", "DDS/DDX in DapRequestHandler::build_dds_from_file: ");
209 if (BESDebug::IsSet("dapreader2")) dds->print_xml(*(BESDebug::GetStrm()), false);
210}
211
212void DapRequestHandler::build_dmr_from_file(const string& accessed, bool explicit_containers, DMR* dmr)
213{
214 BESDEBUG("dapreader", "In DapRequestHandler::build_dmr_from_file; accessed: " << accessed << endl);
215
216 dmr->set_filename(accessed);
217 dmr->set_name(name_path(accessed));
218
219 D4TestTypeFactory TestFactory;
220 D4BaseTypeFactory BaseFactory;
221 if (d_use_test_types) {
222 dmr->set_factory(&TestFactory);
223 }
224 else {
225 dmr->set_factory(&BaseFactory);
226 }
227
228 if ((extension_match(accessed, ".dmr") || extension_match(accessed, ".xml")) && d_use_test_types) {
229 D4ParserSax2 parser;
230 ifstream in(accessed.c_str(), ios::in);
231 parser.intern(in, dmr);
232 }
233 else if (extension_match(accessed, ".dap")) {
234 unique_ptr<D4Connect> url(new D4Connect(accessed));
235 fstream f(accessed.c_str(), std::ios_base::in);
236 if (!f.is_open() || f.bad() || f.eof()) throw Error((string) ("Could not open: ") + accessed);
237
238 Response r(&f, 0);
239 // use the read_data...() method because we need to process the special
240 // binary glop in the data responses.
241 url->read_data_no_mime(*dmr, r);
242 }
243 else if (extension_match(accessed, ".dds") || extension_match(accessed, ".dods")
244 || extension_match(accessed, ".data")) {
245
246 unique_ptr<DDS> dds(new DDS(0 /*factory*/));
247
248 build_dds_from_file(accessed, explicit_containers, dds.get());
249
250 dmr->build_using_dds(*dds);
251 }
252 else {
253 dmr->set_factory(nullptr);
254 throw Error("The dapreader module can only return DMR/DAP responses for files ending in .dmr, .xml or .dap");
255 }
256
257 dmr->set_factory(nullptr);
258}
259
272{
273 BESDEBUG(module, "Entering dap_build_dmr..." << endl);
274
275 BESResponseObject *response = dhi.response_handler->get_response_object();
276 BESDMRResponse *bdmr = dynamic_cast<BESDMRResponse *>(response);
277 if (!bdmr) throw BESInternalError("BESDMRResponse cast error", __FILE__, __LINE__);
278
279 try {
280 build_dmr_from_file(dhi.container->access(), bdmr->get_explicit_containers(), bdmr->get_dmr());
281
282 bdmr->set_dap4_constraint(dhi);
283 bdmr->set_dap4_function(dhi);
284 }
285 catch (BESError &e) {
286 throw e;
287 }
288 catch (InternalErr & e) {
289 throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
290 }
291 catch (Error & e) {
292 throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
293 }
294 catch (...) {
295 throw BESInternalFatalError("Unknown exception caught building a DMR", __FILE__, __LINE__);
296 }
297
298 BESDEBUG(module, "Leaving dap_build_dmr..." << endl);
299
300 return true;
301}
302
303// This method sets the stage for the BES DAP service to return a data
304// response. Unlike the DAP2 data response returned by this module, the
305// data are not read from a 'freeze-dried' DAP data response. Instead
306// they are generated by the D4TestTypeFactory types. So, for now, asking
307// for a DAP4 data response from this handler w/o setting UseTestTypes
308// is an error.
309bool DapRequestHandler::dap_build_dap4data(BESDataHandlerInterface &dhi)
310{
311 BESDEBUG(module, "Entering dap_build_dap4data..." << endl);
312
313 BESResponseObject *response = dhi.response_handler->get_response_object();
314 BESDMRResponse *bdmr = dynamic_cast<BESDMRResponse *>(response);
315 if (!bdmr) throw BESInternalError("BESDMRResponse cast error", __FILE__, __LINE__);
316
317 try {
318 DMR *dmr = bdmr->get_dmr();
319 build_dmr_from_file(dhi.container->access(), bdmr->get_explicit_containers(), dmr);
320
321 if (d_use_series_values) {
322 dmr->root()->set_read_p(false);
323
324 TestCommon *tc = dynamic_cast<TestCommon*>(dmr->root());
325 if (tc)
326 tc->set_series_values(true);
327 else
328 throw Error("In the reader handler: Could not set UseSeriesValues");
329 }
330
331 bdmr->set_dap4_constraint(dhi);
332 bdmr->set_dap4_function(dhi);
333 }
334 catch (BESError &e) {
335 throw e;
336 }
337 catch (InternalErr & e) {
338 throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
339 }
340 catch (Error & e) {
341 throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
342 }
343 catch (...) {
344 throw BESInternalFatalError("Unknown exception caught building DAP4 Data response", __FILE__, __LINE__);
345 }
346
347 BESDEBUG(module, "Leaving dap_build_dap4data..." << endl);
348
349 return false;
350}
351
360{
361 BESResponseObject *response = dhi.response_handler->get_response_object();
362 BESDASResponse *bdas = dynamic_cast<BESDASResponse *>(response);
363 if (!bdas) throw BESInternalError("DAS cast error", __FILE__, __LINE__);
364 try {
366 DAS *das = bdas->get_das();
367 string accessed = dhi.container->access();
368
369 if (extension_match(accessed, ".das")) {
370 das->parse(accessed);
371 }
372 else if (extension_match(accessed, ".dods") || extension_match(accessed, ".data")) {
373 Ancillary::read_ancillary_das(*das, accessed);
374 }
375 else {
376 throw Error(
377 "The dapreader module can only return DAS responses for files ending in .das or .dods/.data.\nIn the latter case there must be an ancillary das file present.");
378 }
379
380 bdas->clear_container();
381 }
382 catch (BESError &e) {
383 throw e;
384 }
385 catch (InternalErr & e) {
386 throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
387 }
388 catch (Error & e) {
389 throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
390 }
391 catch (...) {
392 throw BESInternalFatalError("Unknown exception caught building DAS", __FILE__, __LINE__);
393 }
394
395 return true;
396}
397
398
399bool DapRequestHandler::dap_build_dds(BESDataHandlerInterface &dhi)
400{
401 BESDEBUG(module, "Entering dap_build_dds..." << endl);
402
403 BESResponseObject *response = dhi.response_handler->get_response_object();
404 BESDDSResponse *bdds = dynamic_cast<BESDDSResponse *>(response);
405 if (!bdds) throw BESInternalError("DDS cast error", __FILE__, __LINE__);
406
407 try {
409
410 build_dds_from_file(dhi.container->access(), bdds->get_explicit_containers(), bdds->get_dds());
411
412 bdds->set_constraint(dhi);
413 bdds->clear_container();
414 }
415 catch (BESError &e) {
416 throw e;
417 }
418 catch (InternalErr & e) {
419 throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
420 }
421 catch (Error & e) {
422 throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
423 }
424 catch (...) {
425 throw BESInternalFatalError("Unknown exception caught building DDS", __FILE__, __LINE__);
426 }
427
428 BESDEBUG(module, "Exiting dap_build_dds..." << endl);
429
430 return true;
431}
432
433bool DapRequestHandler::dap_build_data(BESDataHandlerInterface &dhi)
434{
435 BESDEBUG(module, "Entering dap_build_data..." << endl);
436
437 BESResponseObject *response = dhi.response_handler->get_response_object();
438 BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(response);
439 if (!bdds) throw BESInternalError("DDS cast error", __FILE__, __LINE__);
440
441 try {
443
444 build_dds_from_file(dhi.container->access(), bdds->get_explicit_containers(), bdds->get_dds());
445
446 bdds->set_constraint(dhi);
447 bdds->clear_container();
448 }
449 catch (BESError &e) {
450 throw e;
451 }
452 catch (InternalErr & e) {
453 throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
454 }
455 catch (Error & e) {
456 throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
457 }
458 catch (...) {
459 throw BESInternalFatalError("Unknown exception caught building a data response", __FILE__, __LINE__);
460 }
461
462 BESDEBUG(module, "Exiting dap_build_data..." << endl);
463
464 return true;
465}
466
467#if 0
468void DapRequestHandler::add_attributes(BESDataHandlerInterface &dhi) {
469
470 BESResponseObject *response = dhi.response_handler->get_response_object();
471 BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(response);
472 if (!bdds)
473 throw BESInternalError("cast error", __FILE__, __LINE__);
474
475 DDS *dds = bdds->get_dds();
476 string container_name = bdds->get_explicit_containers() ? dhi.container->get_symbolic_name(): "";
477 string dataset_name = dhi.container->access();
478 DAS* das = 0;
479 {
480 das = new DAS;
481 // This looks at the 'use explicit containers' prop, and if true
482 // sets the current container for the DAS.
483 if (!container_name.empty()) das->container_name(container_name);
484
485 nc_read_dataset_attributes(*das, dataset_name);
486 Ancillary::read_ancillary_das(*das, dataset_name);
487
488 dds->transfer_attributes(das);
489
490 // Only free the DAS if it's not added to the cache
491 if (das_cache) {
492 // add a copy
493 BESDEBUG(NC_NAME, "DAS added to the cache for : " << dataset_name << endl);
494 das_cache->add(das, dataset_name);
495 }
496 else {
497 delete das;
498 }
499 }
500
501 BESDEBUG(NC_NAME, "Data ACCESS in add_attributes(): set the including attribute flag to true: "<<dataset_name << endl);
502 bdds->set_ia_flag(true);
503 return;
504}
505#endif
506
507bool DapRequestHandler::dap_build_vers(BESDataHandlerInterface &dhi)
508{
509 BESVersionInfo *info = dynamic_cast<BESVersionInfo *>(dhi.response_handler->get_response_object());
510 if (!info) throw BESInternalFatalError("Expected a BESVersionInfo instance.", __FILE__, __LINE__);
511
512 info->add_module(DAPREADER_PACKAGE, DAPREADER_VERSION);
513 return true;
514}
515
516bool DapRequestHandler::dap_build_help(BESDataHandlerInterface &dhi)
517{
518 BESInfo *info = dynamic_cast<BESInfo *>(dhi.response_handler->get_response_object());
519 if (!info) throw BESInternalFatalError("Expected a BESVersionInfo instance.", __FILE__, __LINE__);
520
521 // This is an example. If you had a help file you could load it like
522 // this and if your handler handled the following responses.
523 map<string, string, std::less<>> attrs;
524 attrs["name"] = DAPREADER_PACKAGE /* PACKAGE_NAME */;
525 attrs["version"] = DAPREADER_VERSION /* PACKAGE_VERSION */;
526 list<string> services;
527 BESServiceRegistry::TheRegistry()->services_handled(module, services);
528 if (services.size() > 0) {
529 string handles = BESUtil::implode(services, ',');
530 attrs["handles"] = handles;
531 }
532 info->begin_tag("module", &attrs);
533 info->end_tag("module");
534
535 return true;
536}
537
539{
540 strm << BESIndent::LMarg << "DapRequestHandler::dump - (" << (void *) this << ")" << endl;
541 BESIndent::Indent();
543 BESIndent::UnIndent();
544}
545
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
Represents an OPeNDAP DAS DAP2 data object within the BES.
virtual void clear_container()
clear the container in the DAP response object
virtual void set_container(const std::string &cn)
set the container in the DAP response object
Holds a DDS object within the BES.
virtual void set_container(const std::string &cn)
set the container in the DAP response object
virtual void clear_container()
clear the container in the DAP response object
libdap::DDS * get_dds()
Represents an OPeNDAP DMR DAP4 data object within the BES.
error object created from libdap error objects and can handle those errors
Definition BESDapError.h:50
virtual void set_dap4_function(BESDataHandlerInterface &dhi)
set the constraint depending on the context
virtual void set_dap4_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
virtual void set_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
bool get_explicit_containers() const
Should containers be explicitly represented in the DD* responses?
virtual void set_container(const std::string &cn)
set the container in the DAP response object
virtual void clear_container()
clear the container in the DAP response object
Structure storing information used by the BES to handle the request.
BESContainer * container
pointer to current container in this interface
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition BESDebug.h:145
static std::ostream * GetStrm()
return the debug stream
Definition BESDebug.h:165
Base exception class for the BES with basic string message.
Definition BESError.h:66
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
Represents a specific data type request handler.
virtual void dump(std::ostream &strm) const
dumps information about this object
virtual BESResponseObject * get_response_object()
return the current response object
Abstract base class representing a specific set of information in response to a request to the BES.
virtual void services_handled(const std::string &handler, std::list< std::string > &services)
returns the list of servies provided by the handler in question
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
static bool dap_build_das(BESDataHandlerInterface &dhi)
static bool dap_build_dmr(BESDataHandlerInterface &dhi)
virtual void dump(std::ostream &strm) const
dumps information about this object
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
STL class.