bes  Updated for version 3.17.4
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 <BaseTypeFactory.h>
50 #include <test/TestTypeFactory.h>
51 #include <D4BaseTypeFactory.h>
52 #include <test/D4TestTypeFactory.h>
53 #include <test/TestCommon.h>
54 
55 #include <DMR.h>
56 #include <D4Group.h>
57 #include <D4Connect.h>
58 #include <D4ParserSax2.h>
59 
60 #include <Ancillary.h>
61 #include <Connect.h>
62 #include <Response.h>
63 #include <InternalErr.h>
64 #include <mime_util.h>
65 
66 using namespace libdap;
67 
68 int test_variable_sleep_interval = 0;
69 
70 bool DapRequestHandler::d_use_series_values = true;
71 bool DapRequestHandler::d_use_series_values_set = false;
72 
73 bool DapRequestHandler::d_use_test_types = true;
74 bool DapRequestHandler::d_use_test_types_set = false;
75 
76 const string module = "dapreader";
77 
78 static 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 
94 static 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.length() == data_source.length();
98 }
99 
100 DapRequestHandler::DapRequestHandler(const string &name) :
101  BESRequestHandler(name)
102 {
103  add_handler(DAS_RESPONSE, dap_build_das);
104  add_handler(DDS_RESPONSE, dap_build_dds);
105  add_handler(DATA_RESPONSE, dap_build_data);
106 
107  add_handler(DMR_RESPONSE, dap_build_dmr);
108  add_handler(DAP4DATA_RESPONSE, dap_build_dap4data);
109 
110  add_handler(VERS_RESPONSE, dap_build_vers);
111  add_handler(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 
123 void 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  if (d_use_test_types)
128  dds.set_factory(new TestTypeFactory); // DDS deletes the factory
129  else
130  dds.set_factory(new BaseTypeFactory);
131 
132  auto_ptr<Connect> url(new Connect(accessed));
133  Response r(fopen(accessed.c_str(), "r"), 0);
134  if (!r.get_stream()) throw Error(string("The input source: ") + accessed + string(" could not be opened"));
135  url->read_data_no_mime(dds, &r);
136 
137  auto_ptr<DAS> das(new DAS);
138  Ancillary::read_ancillary_das(*das, accessed);
139 
140  if (das->get_size() > 0) dds.transfer_attributes(das.get());
141 
142  // This is needed for the values read to show up. Without it the default
143  // behavior of the TestTypes will take over and the values from the data files
144  // will be ignored.
145  for (DDS::Vars_iter i = dds.var_begin(), e = dds.var_end(); i != e; i++) {
146  (*i)->set_read_p(true);
147  }
148 }
149 
157 void DapRequestHandler::build_dds_from_file(const string &accessed, bool explicit_containers, DDS *dds)
158 {
159  BESDEBUG("dapreader", "In DapRequestHandler::build_dds_from_file; accessed: " << accessed << endl);
160 
161  if (extension_match(accessed, ".dds") && d_use_test_types) {
162  dds->set_factory(new TestTypeFactory);
163  dds->parse(accessed); // This sets the dataset name based on what's in the file
164 
165  DAS *das = new DAS;
166  Ancillary::read_ancillary_das(*das, accessed);
167 
168  if (das->get_size() > 0) dds->transfer_attributes(das);
169  }
170  else if (extension_match(accessed, ".dods") || extension_match(accessed, ".data")) {
171  if (explicit_containers) {
172  BESDEBUG("dapreader", "In DapRequestHandler::build_dds_from_file; in container code" << endl);
173  DDS local_dds(0);
174 
175  // This function reads from a .dods, ..., 'frozen response' and loads
176  // the values into a DDS's variables. It then merges the Attributes read
177  // from a matching .das file into those variables. The code in Connect
178  // that reads the values is not 'container safe' so we use this function
179  // to read value into a 'local dds' and then transfer its variables to
180  // the real BESDDSResponseObject, which is the DDS passed to this function
181  load_dds_from_data_file(accessed, local_dds);
182 
183  // Transfer variables just read into BESDDSResponse/BESDataDDSResponse's DDS
184  for (DDS::Vars_iter i = local_dds.var_begin(), e = local_dds.var_end(); i != e; i++) {
185  dds->add_var((*i)); // copy the variables; figure out how to not copy them
186  }
187 
188  dds->set_dataset_name(name_path(accessed));
189  }
190  else {
191  BESDEBUG("dapreader", "In DapRequestHandler::build_dds_from_file; in plain code" << endl);
192  // In the non-container case, reading the values is pretty straightforward
193  load_dds_from_data_file(accessed, *dds);
194  }
195 
196  dds->filename(accessed);
197  }
198  else {
199  throw Error("The dapreader module can only return DDS/DODS responses for files ending in .dods, .data or .dds");
200  }
201 
202  BESDEBUG("dapreader2", "DDS/DDX in DapRequestHandler::build_dds_from_file: ");
203  if (BESDebug::IsSet("dapreader2")) dds->print_xml(*(BESDebug::GetStrm()), false);
204 }
205 
206 void DapRequestHandler::build_dmr_from_file(const string& accessed, bool explicit_containers, DMR* dmr)
207 {
208  BESDEBUG("dapreader", "In DapRequestHandler::build_dmr_from_file; accessed: " << accessed << endl);
209 
210  dmr->set_filename(accessed);
211  dmr->set_name(name_path(accessed));
212 
213  D4TestTypeFactory TestFactory;
214  D4BaseTypeFactory BaseFactory;
215  if (d_use_test_types) {
216  dmr->set_factory(&TestFactory);
217  }
218  else {
219  dmr->set_factory(&BaseFactory);
220  }
221 
222  if ((extension_match(accessed, ".dmr") || extension_match(accessed, ".xml")) && d_use_test_types) {
223  D4ParserSax2 parser;
224  ifstream in(accessed.c_str(), ios::in);
225  parser.intern(in, dmr);
226  }
227  else if (extension_match(accessed, ".dap")) {
228  auto_ptr<D4Connect> url(new D4Connect(accessed));
229  fstream f(accessed.c_str(), std::ios_base::in);
230  if (!f.is_open() || f.bad() || f.eof()) throw Error((string) ("Could not open: ") + accessed);
231 
232  Response r(&f, 0);
233  // use the read_data...() method because we need to process the special
234  // binary glop in the data responses.
235  url->read_data_no_mime(*dmr, r);
236  }
237  else if (extension_match(accessed, ".dds") || extension_match(accessed, ".dods")
238  || extension_match(accessed, ".data")) {
239 
240  auto_ptr<DDS> dds(new DDS(0 /*factory*/));
241 
242  build_dds_from_file(accessed, explicit_containers, dds.get());
243 
244  dmr->build_using_dds(*dds);
245  }
246  else {
247  dmr->set_factory(0);
248  throw Error("The dapreader module can only return DMR/DAP responses for files ending in .dmr, .xml or .dap");
249  }
250 
251  dmr->set_factory(0);
252 }
253 
266 {
267  BESDEBUG(module, "Entering dap_build_dmr..." << endl);
268 
269  BESResponseObject *response = dhi.response_handler->get_response_object();
270  BESDMRResponse *bdmr = dynamic_cast<BESDMRResponse *>(response);
271  if (!bdmr) throw BESInternalError("BESDMRResponse cast error", __FILE__, __LINE__);
272 
273  try {
274  build_dmr_from_file(dhi.container->access(), bdmr->get_explicit_containers(), bdmr->get_dmr());
275 
276  bdmr->set_dap4_constraint(dhi);
277  bdmr->set_dap4_function(dhi);
278  }
279  catch (BESError &e) {
280  throw e;
281  }
282  catch (InternalErr & e) {
283  throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
284  }
285  catch (Error & e) {
286  throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
287  }
288  catch (...) {
289  throw BESInternalFatalError("Unknown exception caught building a DMR", __FILE__, __LINE__);
290  }
291 
292  BESDEBUG(module, "Leaving dap_build_dmr..." << endl);
293 
294  return true;
295 }
296 
297 // This method sets the stage for the BES DAP service to return a data
298 // response. Unlike the DAP2 data response returned by this module, the
299 // data are not read from a 'freeze-dried' DAP data response. Instead
300 // they are generated by the D4TestTypeFactory types. So, for now, asking
301 // for a DAP4 data response from this handler w/o setting UseTestTypes
302 // is an error.
303 bool DapRequestHandler::dap_build_dap4data(BESDataHandlerInterface &dhi)
304 {
305  BESDEBUG(module, "Entering dap_build_dap4data..." << endl);
306 
307  BESResponseObject *response = dhi.response_handler->get_response_object();
308  BESDMRResponse *bdmr = dynamic_cast<BESDMRResponse *>(response);
309  if (!bdmr) throw BESInternalError("BESDMRResponse cast error", __FILE__, __LINE__);
310 
311  try {
312  DMR *dmr = bdmr->get_dmr();
313  build_dmr_from_file(dhi.container->access(), bdmr->get_explicit_containers(), dmr);
314 
315  if (d_use_series_values) {
316  dmr->root()->set_read_p(false);
317 
318  TestCommon *tc = dynamic_cast<TestCommon*>(dmr->root());
319  if (tc)
320  tc->set_series_values(true);
321  else
322  throw Error("In the reader handler: Could not set UseSeriesValues");
323  }
324 
325  bdmr->set_dap4_constraint(dhi);
326  bdmr->set_dap4_function(dhi);
327  }
328  catch (BESError &e) {
329  throw e;
330  }
331  catch (InternalErr & e) {
332  throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
333  }
334  catch (Error & e) {
335  throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
336  }
337  catch (...) {
338  throw BESInternalFatalError("Unknown exception caught building DAP4 Data response", __FILE__, __LINE__);
339  }
340 
341  BESDEBUG(module, "Leaving dap_build_dap4data..." << endl);
342 
343  return false;
344 }
345 
354 {
355  BESResponseObject *response = dhi.response_handler->get_response_object();
356  BESDASResponse *bdas = dynamic_cast<BESDASResponse *>(response);
357  if (!bdas) throw BESInternalError("DAS cast error", __FILE__, __LINE__);
358  try {
360  DAS *das = bdas->get_das();
361  string accessed = dhi.container->access();
362 
363  if (extension_match(accessed, ".das")) {
364  das->parse(accessed);
365  }
366  else if (extension_match(accessed, ".dods") || extension_match(accessed, ".data")) {
367  Ancillary::read_ancillary_das(*das, accessed);
368  }
369  else {
370  throw Error(
371  "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.");
372  }
373 
374  bdas->clear_container();
375  }
376  catch (BESError &e) {
377  throw e;
378  }
379  catch (InternalErr & e) {
380  throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
381  }
382  catch (Error & e) {
383  throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
384  }
385  catch (...) {
386  throw BESInternalFatalError("Unknown exception caught building DAS", __FILE__, __LINE__);
387  }
388 
389  return true;
390 }
391 
392 
393 bool DapRequestHandler::dap_build_dds(BESDataHandlerInterface &dhi)
394 {
395  BESDEBUG(module, "Entering dap_build_dds..." << endl);
396 
397  BESResponseObject *response = dhi.response_handler->get_response_object();
398  BESDDSResponse *bdds = dynamic_cast<BESDDSResponse *>(response);
399  if (!bdds) throw BESInternalError("DDS cast error", __FILE__, __LINE__);
400 
401  try {
403 
404  build_dds_from_file(dhi.container->access(), bdds->get_explicit_containers(), bdds->get_dds());
405 
406  bdds->set_constraint(dhi);
407  bdds->clear_container();
408  }
409  catch (BESError &e) {
410  throw e;
411  }
412  catch (InternalErr & e) {
413  throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
414  }
415  catch (Error & e) {
416  throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
417  }
418  catch (...) {
419  throw BESInternalFatalError("Unknown exception caught building DDS", __FILE__, __LINE__);
420  }
421 
422  BESDEBUG(module, "Exiting dap_build_dds..." << endl);
423 
424  return true;
425 }
426 
427 bool DapRequestHandler::dap_build_data(BESDataHandlerInterface &dhi)
428 {
429  BESDEBUG(module, "Entering dap_build_data..." << endl);
430 
431  BESResponseObject *response = dhi.response_handler->get_response_object();
432  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(response);
433  if (!bdds) throw BESInternalError("DDS cast error", __FILE__, __LINE__);
434 
435  try {
437 
438  build_dds_from_file(dhi.container->access(), bdds->get_explicit_containers(), bdds->get_dds());
439 
440  bdds->set_constraint(dhi);
441  bdds->clear_container();
442  }
443  catch (BESError &e) {
444  throw e;
445  }
446  catch (InternalErr & e) {
447  throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
448  }
449  catch (Error & e) {
450  throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
451  }
452  catch (...) {
453  throw BESInternalFatalError("Unknown exception caught building a data response", __FILE__, __LINE__);
454  }
455 
456  BESDEBUG(module, "Exiting dap_build_data..." << endl);
457 
458  return true;
459 }
460 
461 bool DapRequestHandler::dap_build_vers(BESDataHandlerInterface &dhi)
462 {
463  BESVersionInfo *info = dynamic_cast<BESVersionInfo *>(dhi.response_handler->get_response_object());
464  if (!info) throw BESInternalFatalError("Expected a BESVersionInfo instance.", __FILE__, __LINE__);
465 
466  info->add_module(DAPREADER_PACKAGE, DAPREADER_VERSION);
467  return true;
468 }
469 
470 bool DapRequestHandler::dap_build_help(BESDataHandlerInterface &dhi)
471 {
472  BESInfo *info = dynamic_cast<BESInfo *>(dhi.response_handler->get_response_object());
473  if (!info) throw BESInternalFatalError("Expected a BESVersionInfo instance.", __FILE__, __LINE__);
474 
475  // This is an example. If you had a help file you could load it like
476  // this and if your handler handled the following responses.
477  map<string, string> attrs;
478  attrs["name"] = DAPREADER_PACKAGE /* PACKAGE_NAME */;
479  attrs["version"] = DAPREADER_VERSION /* PACKAGE_VERSION */;
480  list<string> services;
481  BESServiceRegistry::TheRegistry()->services_handled(module, services);
482  if (services.size() > 0) {
483  string handles = BESUtil::implode(services, ',');
484  attrs["handles"] = handles;
485  }
486  info->begin_tag("module", &attrs);
487  info->end_tag("module");
488 
489  return true;
490 }
491 
492 void DapRequestHandler::dump(ostream &strm) const
493 {
494  strm << BESIndent::LMarg << "DapRequestHandler::dump - (" << (void *) this << ")" << endl;
495  BESIndent::Indent();
497  BESIndent::UnIndent();
498 }
499 
exception thrown if an internal error is found and is fatal to the BES
exception thrown if inernal error encountered
static string lowercase(const string &s)
Definition: BESUtil.cc:186
virtual void dump(ostream &strm) const
dumps information about this object
Holds a DDS object within the BES.
virtual void clear_container()
clear the container in the DAP response object
libdap::DDS * get_dds()
virtual void set_dap4_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
static bool dap_build_dmr(BESDataHandlerInterface &dhi)
virtual void set_container(const std::string &cn)
set the container in the DAP response object
virtual string access()=0
returns the true name of this container
virtual void clear_container()
clear the container in the DAP response object
virtual void set_dap4_function(BESDataHandlerInterface &dhi)
set the constraint depending on the context
informational response object
Definition: BESInfo.h:68
static string implode(const list< string > &values, char delim)
Definition: BESUtil.cc:619
virtual BESResponseObject * get_response_object()
return the current response object
Abstract exception class for the BES with basic string message.
Definition: BESError.h:56
static ostream * GetStrm()
return the debug stream
Definition: BESDebug.h:186
virtual void set_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
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:51
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
static bool IsSet(const string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:167
virtual void clear_container()
clear the container in the DAP response object
Represents a specific data type request handler.
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:483
virtual void dump(ostream &strm) const
dumps information about this object
Structure storing information used by the BES to handle the request.
bool get_explicit_containers() const
Should containers be explicitly represented in the DD* responses?
virtual void set_container(const string &cn)
set the container in the DAP response object
Represents an OPeNDAP DAS DAP2 data object within the BES.
static bool dap_build_das(BESDataHandlerInterface &dhi)
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:43
virtual void set_container(const string &cn)
set the container in the DAP response object
Abstract base class representing a specific set of information in response to a request to the BES...
BESContainer * container
pointer to current container in this interface
virtual void services_handled(const string &handler, list< string > &services)
returns the list of servies provided by the handler in question
string get_symbolic_name() const
retrieve the symbolic name for this container
Definition: BESContainer.h:197