OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
DapRequestHandler.cc
Go to the documentation of this file.
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 
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 
101  BESRequestHandler(name)
102 {
106 
109 
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 
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.
304 {
305  BESDEBUG(module, "Entering dap_build_dap4data..." << endl);
306 
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 {
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 
394 {
395  BESDEBUG(module, "Entering dap_build_dds..." << endl);
396 
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 
428 {
429  BESDEBUG(module, "Entering dap_build_data..." << endl);
430 
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 
462 {
463  bool ret = true;
464  BESVersionInfo *info = dynamic_cast<BESVersionInfo *>(dhi.response_handler->get_response_object());
466  return ret;
467 }
468 
470 {
471  bool ret = true;
472  BESInfo *info = dynamic_cast<BESInfo *>(dhi.response_handler->get_response_object());
473 
474  // This is an example. If you had a help file you could load it like
475  // this and if your handler handled the following responses.
476  map<string, string> attrs;
477  attrs["name"] = DAPREADER_PACKAGE /* PACKAGE_NAME */;
478  attrs["version"] = DAPREADER_VERSION /* PACKAGE_VERSION */;
479  list<string> services;
481  if (services.size() > 0) {
482  string handles = BESUtil::implode(services, ',');
483  attrs["handles"] = handles;
484  }
485  info->begin_tag("module", &attrs);
486  info->end_tag("module");
487 
488  return ret;
489 }
490 
491 void DapRequestHandler::dump(ostream &strm) const
492 {
493  strm << BESIndent::LMarg << "DapRequestHandler::dump - (" << (void *) this << ")" << endl;
497 }
498 
bool get_explicit_containers() const
Should containers be explicitly represented in the DD* responses?
brief represents simple text information in a response object, such as version and help inforamtion...
int test_variable_sleep_interval
const string module
#define DAP4DATA_RESPONSE
Definition: BESDapNames.h:86
exception thrown if an internal error is found and is fatal to the BES
#define DAPREADER_VERSION
Definition: config.h:19
exception thrown if inernal error encountered
virtual void dump(ostream &strm) const
dumps information about this object
#define DMR_RESPONSE
Definition: BESDapNames.h:81
string get_symbolic_name() const
retrieve the symbolic name for this container
Definition: BESContainer.h:197
static string lowercase(const string &s)
Convert a string to all lower case.
Definition: BESUtil.cc:182
Represents an OPeNDAP DDS DAP2 data object within the BES.
virtual void clear_container()
clear the container in the DAP response object
static bool dap_build_data(BESDataHandlerInterface &dhi)
#define DAPREADER_PACKAGE
Definition: config.h:16
virtual void set_dap4_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
static bool dap_build_dmr(BESDataHandlerInterface &dhi)
Given a request for the DMR response, look at the data source and parse it's DMR/XML information...
static void Indent()
Definition: BESIndent.cc:38
static class NCMLUtil overview
virtual string access()=0
returns the true name of this container
virtual void clear_container()
clear the container in the DAP response object
#define HELP_RESPONSE
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)
implode a list of values into a single string delimited by delim
Definition: BESUtil.cc:602
DapRequestHandler(const string &name)
virtual BESResponseObject * get_response_object()
return the current response object
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
#define DATA_RESPONSE
Definition: BESDapNames.h:70
static BESServiceRegistry * TheRegistry()
static ostream * GetStrm()
return the debug stream
Definition: BESDebug.h:179
BESResponseHandler * response_handler
static bool dap_build_dap4data(BESDataHandlerInterface &dhi)
virtual void set_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
virtual void begin_tag(const string &tag_name, map< string, string > *attrs=0)
Definition: BESInfo.cc:127
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
Represents an OPeNDAP DMR DAP4 data object within the BES.
virtual void set_container(const string &cn)
set the container in the DAP response object
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:160
virtual void clear_container()
clear the container in the DAP response object
Represents a specific data type request handler.
static bool dap_build_help(BESDataHandlerInterface &dhi)
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:453
Structure storing information used by the BES to handle the request.
static bool dap_build_dds(BESDataHandlerInterface &dhi)
#define VERS_RESPONSE
virtual void set_container(const string &cn)
set the container in the DAP response object
virtual bool add_handler(const string &handler_name, p_request_handler handler_method)
add a handler method to the request handler that knows how to fill in a specific response object ...
Represents an OPeNDAP DAS DAP2 data object within the BES.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
static bool dap_build_das(BESDataHandlerInterface &dhi)
This method will look at the extension on the input file and assume that if it's .das, that file should be read and used to build the DAS object.
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:48
virtual void set_container(const string &cn)
set the container in the DAP response object
virtual void dump(ostream &strm) const
dumps information about this object
virtual void add_module(const string &n, const string &v)
Abstract base class representing a specific set of information in response to a request to the BES...
#define DDS_RESPONSE
Definition: BESDapNames.h:60
BESContainer * container
pointer to current container in this interface
#define DAS_RESPONSE
Definition: BESDapNames.h:55
virtual void end_tag(const string &tag_name)
Definition: BESInfo.cc:132
static bool dap_build_vers(BESDataHandlerInterface &dhi)
virtual void services_handled(const string &handler, list< string > &services)
returns the list of servies provided by the handler in question