OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESAsciiTransmit.cc
Go to the documentation of this file.
1 // BESAsciiTransmit.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <memory>
34 
35 #include <BaseType.h>
36 #include <Sequence.h>
37 #include <ConstraintEvaluator.h>
38 #include <D4Group.h>
39 #include <DMR.h>
40 //#include <D4CEDriver.h>
41 #include <D4ConstraintEvaluator.h>
42 #include <crc.h>
43 #include <InternalErr.h>
44 #include <util.h>
45 #include <escaping.h>
46 #include <mime_util.h>
47 
48 #include <BESDapNames.h>
49 #include <BESDataNames.h>
50 #include <BESDapTransmit.h>
51 #include <BESContainer.h>
52 #include <BESDataDDSResponse.h>
53 #include <BESDMRResponse.h>
54 #include <BESDapResponseBuilder.h>
55 
56 #include <BESDapError.h>
57 #include <BESInternalFatalError.h>
58 
59 #include <BESDebug.h>
60 
61 #include "BESAsciiTransmit.h"
62 #include "get_ascii.h"
63 #include "get_ascii_dap4.h"
64 
65 using namespace dap_asciival;
66 
69 {
70 
73 
74 }
75 
76 // This version of send_basic_ascii() should work for server functions,
77 // including ones that are used in combination with a selection expression.
78 // This functionality has not yet been added to the DAP4 version of the
79 // method, however.
80 //
81 // I have some questions regarding how caching will work in this function.
82 //
83 // Since this 'transmitter' pattern is pretty common in our code, I think
84 // it would be good if BESDapResponseBuilder supported it with a set of
85 // methods that could be used to implement the logic uniformly.
87 {
88  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii; modified" << endl);
89 
90  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
91  DataDDS *dds = bdds->get_dds();
92  ConstraintEvaluator &ce = bdds->get_ce();
93 
94  dhi.first_container();
95 
96  string constraint = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
97  BESDEBUG("ascii", "parsing constraint: " << constraint << endl);
98 
100 
101  rb.split_ce(ce, constraint);
102 
103  // If there are functions, parse them and eval.
104  // Use that DDS and parse the non-function ce
105  // Serialize using the second ce and the second dds
106  if (!rb.get_btp_func_ce().empty()) {
107  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii - Found function(s) in CE: " << rb.get_btp_func_ce() << endl);
108 
109  // Define a local ce evaluator so that the clause(s) from the function parse
110  // won't get treated like selection clauses later on when serialize is called
111  // on the DDS (fdds)
112  ConstraintEvaluator func_eval;
113 
114  // FIXME Does caching work outside of the DAP module?
115 #if 0
116  if (responseCache()) {
117  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii - Using the cache for the server function CE" << endl);
118  fdds = rb.responseCache()->cache_dataset(dds, get_btp_func_ce(), this, &func_eval, cache_token);
119  }
120  else {
121  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii - Cache not found; (re)calculating" << endl);
122  func_eval.parse_constraint(get_btp_func_ce(), dds);
123  fdds = func_eval.eval_function_clauses(dds);
124  }
125 #endif
126  func_eval.parse_constraint(rb.get_btp_func_ce(), *dds);
127  DataDDS *fdds = func_eval.eval_function_clauses(*dds);
128 
129  // Server functions might mark variables to use their read()
130  // methods. Clear that so the CE in d_dap2ce will control what is
131  // sent. If that is empty (there was only a function call) all
132  // of the variables in the intermediate DDS (i.e., the function
133  // result) will be sent.
134  fdds->mark_all(false);
135 
136  ce.parse_constraint(rb.get_ce(), *fdds);
137 
138  fdds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
139 
140  int response_size_limit = dds->get_response_limit(); // use the original DDS
141  if (response_size_limit != 0 && fdds->get_request_size(true) > response_size_limit) {
142  string msg = "The Request for " + long_to_string(fdds->get_request_size(true) / 1024)
143  + "KB is too large; requests for this user are limited to "
144  + long_to_string(response_size_limit / 1024) + "KB.";
145  throw Error(msg);
146  }
147 
148  // Now we have the correct values in fdds, so set the BESResponseObject so
149  // that it will reference that. At a minimum, its dtor will free the object.
150  // So, delete the initial DataDDS*
151  bdds->set_dds(fdds);
152  delete dds;
153  dds = fdds;
154 
155  // FIXME Caching broken outside of the DAP module?
156 #if 0
157  if (!store_dap2_result(data_stream, dds, eval)) {
158  serialize_dap2_data_dds(data_stream, *fdds, eval, true /* was 'false'. jhrg 3/10/15 */);
159  }
160 
161  if (responseCache()) responseCache()->unlock_and_close(cache_token);
162 #endif
163  }
164  else {
165  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii - Simple constraint" << endl);
166 
167  ce.parse_constraint(rb.get_ce(), *dds); // Throws Error if the ce doesn't parse.
168 
169  dds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
170 
171  if (dds->get_response_limit() != 0 && dds->get_request_size(true) > dds->get_response_limit()) {
172  string msg = "The Request for " + long_to_string(dds->get_request_size(true) / 1024)
173  + "KB is too large; requests for this user are limited to "
174  + long_to_string(dds->get_response_limit() / 1024) + "KB.";
175  throw Error(msg);
176  }
177 
178  // FIXME Caching...
179 #if 0
180  if (!store_dap2_result(data_stream, dds, eval)) {
181  serialize_dap2_data_dds(data_stream, dds, eval);
182  }
183 #endif
184  }
185 
186  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
187  if ((*i)->send_p()) {
188  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii; call to intern_data() for '" << (*i)->name() << "'" << endl);
189  (*i)->intern_data(ce, *dds);
190  }
191  }
192 
193  try {
194  // Now that we have constrained the DataDDS and read in the data,
195  // send it as ascii
196  DataDDS *ascii_dds = datadds_to_ascii_datadds(dds);
197 
199 
200  dhi.get_output_stream() << flush;
201  delete ascii_dds;
202  }
203  catch (InternalErr &e) {
204  throw BESDapError("Failed to get values as ascii: " + e.get_error_message(), true, e.get_error_code(), __FILE__,
205  __LINE__);
206  }
207  catch (Error &e) {
208  throw BESDapError("Failed to get values as ascii: " + e.get_error_message(), false, e.get_error_code(),
209  __FILE__, __LINE__);
210  }
211  catch (...) {
212  throw BESInternalFatalError("Failed to get values as ascii: Unknown exception caught", __FILE__, __LINE__);
213  }
214 }
215 
216 #if 0
217 // The original version. jhrg 3/9/15
219 {
220  BESDEBUG("ascii", "BESAsciiTransmit::send_base_ascii" << endl);
221 
222  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
223  DataDDS *dds = bdds->get_dds();
224  ConstraintEvaluator & ce = bdds->get_ce();
225 
226  dhi.first_container();
227 
228  string constraint = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
229  BESDEBUG("ascii", "parsing constraint: " << constraint << endl);
230 
232 
233  rb->split_ce(ce, constraint);
234 
235  try {
236  ce.parse_constraint(constraint, *dds);
237  }
238  catch (InternalErr &e) {
239  throw BESDapError("Failed to parse the constraint expression: " + e.get_error_message(), true,
240  e.get_error_code(), __FILE__, __LINE__);
241  }
242  catch (Error &e) {
243  throw BESDapError("Failed to parse the constraint expression: " + e.get_error_message(), false,
244  e.get_error_code(), __FILE__, __LINE__);
245  }
246  catch (...) {
247  throw BESInternalFatalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__,
248  __LINE__);
249  }
250 
251  dds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
252 
253  // string dataset_name = dhi.container->access();
254 
255  try {
256  // Handle *functional* constraint expressions specially
257  if (ce.function_clauses()) {
258  BESDEBUG("ascii", "processing a functional constraint clause(s)." << endl);
259  // This leaks the DDS on the LHS, I think. jhrg 7/29/14
260  // Yes, eval_function_clauses() allocates a new DDS object which
261  // it returns. This code was assigning that to 'dds' which is
262  // a pointer to the DataDDS managed by the BES. The fix is to
263  // store that in a temp, pass that new pointer into the BES object,
264  // delete the old object held by the BES and then set the local pointer
265  // so that the remaining code can use it. See ticket 2240. All of the
266  // Transmitter functions in BES modules will need this fix if they
267  // call eval_function_clauses(). jhrg 7/30/14
268  DataDDS *new_dds = ce.eval_function_clauses(*dds);
269  bdds->set_dds(new_dds);
270  delete dds;
271  dds = new_dds;
272  }
273  else {
274  BESDEBUG("ascii", "processing normal constraint." << endl);
275  // Iterate through the variables in the DataDDS and read in the data
276  // if the variable has the send flag set.
277  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
278  if ((*i)->send_p()) {
279  (**i).intern_data(ce, *dds);
280  }
281  }
282  }
283  }
284 
285  catch (InternalErr &e) {
286  throw BESDapError("Failed to read data: " + e.get_error_message(), true, e.get_error_code(), __FILE__,
287  __LINE__);
288  }
289  catch (Error & e) {
290  throw BESDapError("Failed to read data: " + e.get_error_message(), false, e.get_error_code(), __FILE__,
291  __LINE__);
292  }
293  catch (...) {
294  throw BESInternalFatalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
295  }
296 
297  try {
298  // Now that we have constrained the DataDDS and read in the data,
299  // send it as ascii
300  DataDDS *ascii_dds = datadds_to_ascii_datadds(dds);
301 
303 
304  dhi.get_output_stream() << flush;
305  delete ascii_dds;
306  }
307  catch (InternalErr &e) {
308  throw BESDapError("Failed to get values as ascii: " + e.get_error_message(), true, e.get_error_code(), __FILE__,
309  __LINE__);
310  }
311  catch (Error &e) {
312  throw BESDapError("Failed to get values as ascii: " + e.get_error_message(), false, e.get_error_code(),
313  __FILE__, __LINE__);
314  }
315  catch (...) {
316  throw BESInternalFatalError("Failed to get values as ascii: Unknown exception caught", __FILE__, __LINE__);
317  }
318 }
319 #endif
320 
325 {
326  BESDEBUG("ascii", "BESAsciiTransmit::send_dap4_csv" << endl);
327 
328  BESDMRResponse *bdmr = dynamic_cast<BESDMRResponse *>(obj);
329  DMR *dmr = bdmr->get_dmr();
330 
331  string dap4Constraint = www2id(dhi.data[DAP4_CONSTRAINT], "%", "%20%26");
332  string dap4Function = www2id(dhi.data[DAP4_FUNCTION], "%", "%20%26");
333 
334  // Not sure we need this...
335  dhi.first_container();
336 
337  try {
338  // Handle *functional* constraint expressions specially
339  // Use the D4FunctionDriver class and evaluate the functions, building
340  // an new DMR, then evaluate the D4CE in the context of that DMR.
341  // This might be coded as "if (there's a function) do this else process the CE".
342  // Or it might be coded as "if (there's a function) build the new DMR, then fall
343  // through and process the CE but on the new DMR". jhrg 9/3/14
344 
345  if (!dap4Constraint.empty()) {
346  D4ConstraintEvaluator d4ce(dmr);
347  d4ce.parse(dap4Constraint);
348  }
349  else {
350  dmr->root()->set_send_p(true);
351  }
352 
354  dhi.get_output_stream() << flush;
355  }
356  catch (InternalErr &e) {
357  throw BESDapError("Failed to return values as ascii: " + e.get_error_message(), true, e.get_error_code(),
358  __FILE__, __LINE__);
359  }
360  catch (Error &e) {
361  throw BESDapError("Failed to return values as ascii: " + e.get_error_message(), false, e.get_error_code(),
362  __FILE__, __LINE__);
363  }
364  catch (...) {
365  throw BESInternalFatalError("Failed to return values as ascii: Unknown exception caught", __FILE__, __LINE__);
366  }
367 
368  BESDEBUG("ascii", "Done BESAsciiTransmit::send_dap4_csv" << endl);
369 }
370 
#define DATA_SERVICE
Definition: BESDapNames.h:71
void get_data_values_as_ascii(DataDDS *dds, ostream &strm)
Using the AsciiOutput::print_ascii(), write the data values to an output file/stream as ASCII...
Definition: get_ascii.cc:68
virtual std::string get_ce() const
Return the entire DAP2 constraint expression in a string.
void set_dds(DataDDS *ddsIn)
Set the response object's DDS.
exception thrown if an internal error is found and is fatal to the BES
virtual std::string get_btp_func_ce() const
virtual libdap::DDS * cache_dataset(libdap::DDS &dds, const std::string &constraint, BESDapResponseBuilder *rb, libdap::ConstraintEvaluator *eval, std::string &cache_token)
Get the cached DDS object.
virtual bool add_method(string method_name, p_transmitter trans_method)
static void send_basic_ascii(BESResponseObject *obj, BESDataHandlerInterface &dhi)
DataDDS * datadds_to_ascii_datadds(DataDDS *dds)
Definition: get_ascii.cc:85
virtual BESDapResponseCache * responseCache()
Lazy getter for the ResponseCache.
#define DAP4_CONSTRAINT
Definition: BESDataNames.h:47
#define DAP4_FUNCTION
Definition: BESDataNames.h:46
virtual void split_ce(libdap::ConstraintEvaluator &eval, const std::string &expr="")
Split the CE so that the server functions that compute new values are separated into their own string...
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 void send_dap4_csv(BESResponseObject *obj, BESDataHandlerInterface &dhi)
Transmits DAP4 Data as Comma Separated Values.
#define POST_CONSTRAINT
Definition: BESDataNames.h:43
ConstraintEvaluator & get_ce()
Structure storing information used by the BES to handle the request.
map< string, string > data
the map of string data that will be required for the current request.
This class is used to build responses for/by the BES.
void first_container()
set the container pointer to the first container in the containers list
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
void print_values_as_ascii(DMR *dmr, ostream &strm)
For each variable in the DMR, write out the CSV/ASCII representation for it's data.
#define DAP4DATA_SERVICE
Definition: BESDapNames.h:87
Abstract base class representing a specific set of information in response to a request to the BES...