OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
NCMLRequestHandler.cc
Go to the documentation of this file.
1 // This file is part of the "NcML Module" project, a BES module designed
3 // to allow NcML files to be used to be used as a wrapper to add
4 // AIS to existing datasets of any format.
5 //
6 // Copyright (c) 2009 OPeNDAP, Inc.
7 // Author: Michael Johnson <m.johnson@opendap.org>
8 //
9 // For more information, please also see the main website: http://opendap.org/
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 //
25 // Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26 //
27 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29 #include "config.h"
30 
31 #include <memory>
32 
33 #include <DMR.h>
34 #include <mime_util.h>
35 #include <D4BaseTypeFactory.h>
36 
37 #include "NCMLRequestHandler.h"
38 
39 #include <BESConstraintFuncs.h>
40 #include <BESContainerStorage.h>
42 #include <BESDapNames.h>
43 #include "BESDataDDSResponse.h"
44 #include <BESDataNames.h>
45 #include <BESDASResponse.h>
46 #include <BESDDSResponse.h>
47 #include <BESDMRResponse.h>
48 
49 #include <BESDebug.h>
50 #include <BESInternalError.h>
51 #include <BESDapError.h>
52 #include <BESRequestHandlerList.h>
53 #include <BESResponseHandler.h>
54 #include <BESResponseNames.h>
55 #include <BESServiceRegistry.h>
56 #include <BESTextInfo.h>
57 #include <BESUtil.h>
58 #include <BESVersionInfo.h>
59 #include <TheBESKeys.h>
60 
61 #include "DDSLoader.h"
62 
63 #include "NCMLDebug.h"
64 #include "NCMLUtil.h"
65 #include "NCMLParser.h"
66 #include "NCMLResponseNames.h"
67 #include "SimpleLocationParser.h"
68 
69 using namespace agg_util;
70 using namespace ncml_module;
71 
72 bool NCMLRequestHandler::_global_attributes_container_name_set = false;
73 string NCMLRequestHandler::_global_attributes_container_name = "";
74 
75 NCMLRequestHandler::NCMLRequestHandler(const string &name) :
76  BESRequestHandler(name)
77 {
81 
84 
87 
88  if (NCMLRequestHandler::_global_attributes_container_name_set == false) {
89  bool key_found = false;
90  string value;
91  TheBESKeys::TheKeys()->get_value("NCML.GlobalAttributesContainerName", value, key_found);
92  if (key_found) {
93  // It was set in the conf file
94  NCMLRequestHandler::_global_attributes_container_name_set = true;
95 
96  NCMLRequestHandler::_global_attributes_container_name = value;
97  }
98  }
99 }
100 
102 {
103 }
104 
105 #if 0
106 // Not used. jhrg 4/16/14
107 
108 // This is the original example from Patrick or James for loading local file within the BES...
109 // Still used by DataDDS call, but the other callbacks use DDSLoader
110 // to get a brandy new DDX response.
111 // @see DDSLoader
112 bool NCMLRequestHandler::ncml_build_redirect(BESDataHandlerInterface &dhi, const string& location)
113 {
114  // The current container in dhi is a reference to the ncml file.
115  // Need to parse the ncml file here and get the list of locations
116  // that we will be using. Any constraints defined?
117 
118  // do this for each of the locations retrieved from the ncml file.
119  // If there are more than one locations in the ncml then we can't
120  // set the context for dap_format to dap2. This will create a
121  // structure for each of the locations in the resulting dap object.
122  string sym_name = dhi.container->get_symbolic_name();
124  BESContainerStorage *store = store_list->find_persistence("catalog");
125  if (!store) {
126  throw BESInternalError("couldn't find the catalog storage", __FILE__, __LINE__);
127  }
128  // this will throw an exception if the location isn't found in the
129  // catalog. Might want to catch this. Wish the add returned the
130  // container object created. Might want to change it.
131  string new_sym = sym_name + "_location1";
132  store->add_container(new_sym, location, "");
133 
134  BESContainer *container = store->look_for(new_sym);
135  if (!container) {
136  throw BESInternalError("couldn't find the container" + sym_name, __FILE__, __LINE__);
137  }
138  BESContainer *ncml_container = dhi.container;
139  dhi.container = container;
140 
141  // this will throw an exception if there is a problem building the
142  // response for this container. Might want to catch this
144 
145  // clean up
146  dhi.container = ncml_container;
147  store->del_container(new_sym);
148 
149  return true;
150 }
151 #endif
152 
153 // Here we load the DDX response with by hijacking the current dhi via DDSLoader
154 // and hand it to our parser to load the ncml, load the DDX for the location,
155 // apply ncml transformations to it, then return the modified DDS.
157 {
158  string filename = dhi.container->access();
159 
160  // Any exceptions winding through here will cause the loader and parser dtors
161  // to clean up dhi state, etc.
162  DDSLoader loader(dhi);
163  NCMLParser parser(loader);
164  auto_ptr<BESDapResponse> loaded_bdds = parser.parse(filename, DDSLoader::eRT_RequestDDX);
165 
166  if (!(loaded_bdds.get())) {
167  throw BESInternalError("Null BESDDSResponse in ncml DAS handler.", __FILE__, __LINE__);
168  }
169 
170  // Now fill in the desired DAS response object from the DDS
171  DDS* dds = NCMLUtil::getDDSFromEitherResponse(loaded_bdds.get());
172  VALID_PTR(dds);
174  BESDASResponse *bdas = dynamic_cast<BESDASResponse *>(response);
175  VALID_PTR(bdas);
176 
177  // Copy the modified DDS attributes into the DAS response object!
178  DAS *das = bdas->get_das();
179  BESDEBUG("ncml", "Creating DAS response from the DDS/X..." << endl);
180 
181  if (dds->get_dap_major() < 4)
182  NCMLUtil::hackGlobalAttributesForDAP2(dds->get_attr_table(),
184 
185  NCMLUtil::populateDASFromDDS(das, *dds);
186 
187  // loaded_bdds destroys itself.
188  return true;
189 }
190 
192 {
193  string filename = dhi.container->access();
194 
195  // Any exceptions winding through here will cause the loader and parser dtors
196  // to clean up dhi state, etc.
197  auto_ptr<BESDapResponse> loaded_bdds(0);
198  {
199  DDSLoader loader(dhi);
200  NCMLParser parser(loader);
201  loaded_bdds = parser.parse(filename, DDSLoader::eRT_RequestDDX);
202  }
203  if (!loaded_bdds.get()) {
204  throw BESInternalError("Null BESDDSResonse in ncml DDS handler.", __FILE__, __LINE__);
205  }
206 
207  // Poke the handed down original response object with the loaded and modified one.
208  DDS* dds = NCMLUtil::getDDSFromEitherResponse(loaded_bdds.get());
209  VALID_PTR(dds);
211  BESDDSResponse *bdds_out = dynamic_cast<BESDDSResponse *>(response);
212  VALID_PTR(bdds_out);
213  DDS *dds_out = bdds_out->get_dds();
214  VALID_PTR(dds_out);
215 
216  if (dds->get_dap_major() < 4)
217  NCMLUtil::hackGlobalAttributesForDAP2(dds->get_attr_table(),
219 
220  // If we just use DDS::operator=, we get into trouble with copied
221  // pointers, bashing of the dataset name, etc etc so I specialize the copy for now.
223 
224  // Apply constraints to the result
226  //bdds->set_constraint(dhi);
227 
228  // Also copy in the name of the original ncml request
229  // TODO @HACK Not sure I want just the basename for the filename,
230  // but since the DDS/DataDDS response fills the dataset name with it,
231  // Our bes-testsuite fails since we get local path info in the dataset name.
232  dds_out->filename(name_path(filename));
233  dds_out->set_dataset_name(name_path(filename));
234 
235  return true;
236 }
237 
239 {
240  string filename = dhi.container->access();
242  // it better be a data response!
243  BESDataDDSResponse* dataResponse = dynamic_cast<BESDataDDSResponse *>(theResponse);
244  NCML_ASSERT_MSG(dataResponse,
245  "NCMLRequestHandler::ncml_build_data(): expected BESDataDDSResponse* but didn't get it!!");
246 
247  // Block it up to force cleanup of DHI.
248  {
249  DDSLoader loader(dhi);
250  NCMLParser parser(loader);
251  parser.parseInto(filename, DDSLoader::eRT_RequestDataDDS, dataResponse);
252  }
253 
254  DDS* dds = NCMLUtil::getDDSFromEitherResponse(dataResponse);
255  VALID_PTR(dds);
256 
257  // Apply constraints to the result
259  //bdds->set_constraint(dhi);
260 
261  // Also copy in the name of the original ncml request
262  // TODO @HACK Not sure I want just the basename for the filename,
263  // but since the DDS/DataDDS response fills the dataset name with it,
264  // Our bes-testsuite fails since we get local path info in the dataset name.
265  dds->filename(name_path(filename));
266  dds->set_dataset_name(name_path(filename));
267  return true;
268 }
269 
271 {
272  // Because this code does not yet know how to build a DMR directly, use
273  // the DMR ctor that builds a DMR using a 'full DDS' (a DDS with attributes).
274  // First step, build the 'full DDS'
275  string data_path = dhi.container->access();
276 
277  DDS *dds = 0; // This will be deleted when loaded_bdds goes out of scope.
278  auto_ptr<BESDapResponse> loaded_bdds(0);
279  try {
280  DDSLoader loader(dhi);
281  NCMLParser parser(loader);
282  loaded_bdds = parser.parse(data_path, DDSLoader::eRT_RequestDDX);
283  if (!loaded_bdds.get()) throw BESInternalError("Null BESDDSResonse in ncml DDS handler.", __FILE__, __LINE__);
284  dds = NCMLUtil::getDDSFromEitherResponse(loaded_bdds.get());
285  VALID_PTR(dds);
286  dds->filename(data_path);
287  dds->set_dataset_name(data_path);
288  }
289  catch (InternalErr &e) {
290  throw BESDapError(e.get_error_message(), true, e.get_error_code(), __FILE__, __LINE__);
291  }
292  catch (Error &e) {
293  throw BESDapError(e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
294  }
295  catch (...) {
296  throw BESDapError("Caught unknown error build ** DMR response", true, unknown_error, __FILE__, __LINE__);
297  }
298 
299  // Extract the DMR Response object - this holds the DMR used by the
300  // other parts of the framework.
302  BESDMRResponse &bdmr = dynamic_cast<BESDMRResponse &>(*response);
303 
304  // Get the DMR made by the BES in the BES/dap/BESDMRResponseHandler, make sure there's a
305  // factory we can use and then dump the DAP2 variables and attributes in using the
306  // BaseType::transform_to_dap4() method that transforms individual variables
307  DMR *dmr = bdmr.get_dmr();
308  dmr->set_factory(new D4BaseTypeFactory);
309  dmr->build_using_dds(*dds);
310 
311  // Instead of fiddling with the internal storage of the DHI object,
312  // (by setting dhi.data[DAP4_CONSTRAINT], etc., directly) use these
313  // methods to set the constraints. But, why? Ans: from Patrick is that
314  // in the 'container' mode of BES each container can have a different
315  // CE.
316  bdmr.set_dap4_constraint(dhi);
317  bdmr.set_dap4_function(dhi);
318 
319  return true;
320 }
321 
323 {
324  BESVersionInfo *info = dynamic_cast<BESVersionInfo *>(dhi.response_handler->get_response_object());
325  if (!info) throw InternalErr(__FILE__, __LINE__, "Expected a BESVersionInfo instance");
326 #if 0
328 #endif
329  info->add_module(MODULE_NAME, MODULE_VERSION);
330  return true;
331 }
332 
334 {
335  BESInfo *info = dynamic_cast<BESInfo *>(dhi.response_handler->get_response_object());
336  if (!info) throw InternalErr(__FILE__, __LINE__, "Expected a BESVersionInfo instance");
337 
338  // This is an example. If you had a help file you could load it like
339  // this and if your handler handled the following responses.
340  map<string, string> attrs;
341  attrs["name"] = MODULE_NAME ;
342  attrs["version"] = MODULE_VERSION ;
343 #if 0
344  attrs["name"] = PACKAGE_NAME;
345  attrs["version"] = PACKAGE_VERSION;
346 #endif
347  list<string> services;
349  if (services.size() > 0) {
350  string handles = BESUtil::implode(services, ',');
351  attrs["handles"] = handles;
352  }
353  info->begin_tag("module", &attrs);
354  //info->add_data_from_file( "NCML.Help", "NCML Help" ) ;
355  info->add_data("Please consult the online documentation at " + ncml_module::ModuleConstants::DOC_WIKI_URL);
356  info->end_tag("module");
357 
358  return true;
359 }
360 
361 void NCMLRequestHandler::dump(ostream &strm) const
362 {
363  strm << BESIndent::LMarg << "NCMLRequestHandler::dump - (" << (void *) this << ")" << endl;
367 }
368 
brief represents simple text information in a response object, such as version and help inforamtion...
virtual void dump(ostream &strm) const
dumps information about this object
string get_constraint() const
retrieve the constraint expression for this container
Definition: BESContainer.h:170
static void copyVariablesAndAttributesInto(libdap::DDS *dds_out, const libdap::DDS &dds_in)
Make a deep copy of the global attributes and variables within dds_in into *dds_out.
Definition: NCMLUtil.cc:256
provides persistent storage for data storage information represented by a container.
#define DAP4DATA_RESPONSE
Definition: BESDapNames.h:86
exception thrown if inernal error encountered
static const std::string DOC_WIKI_URL
URL with the location of the documentation Wiki.
#define DMR_RESPONSE
Definition: BESDapNames.h:81
virtual BESContainerStorage * find_persistence(const string &persist_name)
find the persistence store with the given name
string get_symbolic_name() const
retrieve the symbolic name for this container
Definition: BESContainer.h:197
Represents an OPeNDAP DDS DAP2 data object within the BES.
virtual BESContainer * look_for(const string &sym_name)=0
looks for a container in this persistent store
An abstract superclass for NCMLArray that handles the non-parameterized functionality and allows u...
static string get_global_attributes_container_name()
static bool ncml_build_vers(BESDataHandlerInterface &dhi)
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
virtual void set_dap4_constraint(BESDataHandlerInterface &dhi)
set the constraint depending on the context
static libdap::DDS * getDDSFromEitherResponse(BESDapResponse *response)
Return the DDS* for the given response object.
Definition: NCMLUtil.cc:282
#define NCML_ASSERT_MSG(cond, msg)
Definition: NCMLDebug.h:83
static void Indent()
Definition: BESIndent.cc:38
virtual string access()=0
returns the true name of this container
#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
virtual BESResponseObject * get_response_object()
return the current response object
#define PACKAGE_NAME
Definition: config.h:244
#define DATA_RESPONSE
Definition: BESDapNames.h:70
static BESServiceRegistry * TheRegistry()
BESResponseHandler * response_handler
Provides a mechanism for accessing container information from different container stores registered w...
static bool ncml_build_dds(BESDataHandlerInterface &dhi)
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.
error object created from libdap error objects and can handle those errors
Definition: BESDapError.h:51
static bool ncml_build_das(BESDataHandlerInterface &dhi)
virtual void add_container(const string &sym_name, const string &real_name, const string &type)=0
adds a container with the provided information
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
static bool ncml_build_data(BESDataHandlerInterface &dhi)
void parseInto(const string &ncmlFilename, agg_util::DDSLoader::ResponseType responseType, BESDapResponse *response)
Same as parse, but the response object to parse into is passed down by the caller rather than created...
Definition: NCMLParser.cc:184
Represents a specific data type request handler.
static void populateDASFromDDS(libdap::DAS *das, const libdap::DDS &dds_const)
Given we have a valid attribute tree inside of the DDS, recreate it in the DAS.
Definition: NCMLUtil.cc:200
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:453
#define POST_CONSTRAINT
Definition: BESDataNames.h:43
static BESRequestHandlerList * TheList()
Structure storing information used by the BES to handle the request.
static BESContainerStorageList * TheList()
map< string, string > data
the map of string data that will be required for the current request.
virtual void execute_current(BESDataHandlerInterface &dhi)
Execute a single method for the current container that will fill in the response object rather than i...
static void hackGlobalAttributesForDAP2(libdap::AttrTable &global_attributes, const std::string &global_container_name)
Definition: NCMLUtil.cc:318
#define VERS_RESPONSE
virtual void add_data(const string &s)
add data to this informational object.
Definition: BESInfo.cc:148
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 ...
#define VALID_PTR(ptr)
Definition: NCMLDebug.h:88
#define PACKAGE_VERSION
Definition: config.h:256
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
virtual bool del_container(const string &s_name)=0
removes a container with the given symbolic name
A container is something that holds data.
Definition: BESContainer.h:60
static bool ncml_build_help(BESDataHandlerInterface &dhi)
static void UnIndent()
Definition: BESIndent.cc:44
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:48
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...
static const std::string NCML_NAME
The name used to specify an ncml file.
static bool ncml_build_dmr(BESDataHandlerInterface &dhi)
auto_ptr< BESDapResponse > parse(const string &ncmlFilename, agg_util::DDSLoader::ResponseType type)
Parse the NcML filename, returning a newly allocated DDS response containing the underlying dataset t...
Definition: NCMLParser.cc:170
#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
virtual void services_handled(const string &handler, list< string > &services)
returns the list of servies provided by the handler in question