OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESDapResponseBuilder.cc
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
4 // Access Protocol.
5 
6 // Copyright (c) 2011 OPeNDAP, Inc.
7 // Author: James Gallagher <jgallagher@opendap.org>
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 OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24 
25 #include "config.h"
26 
27 #include <signal.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <uuid/uuid.h> // used to build CID header value for data ddx
31 
32 #ifndef WIN32
33 #include <sys/wait.h>
34 #else
35 #include <io.h>
36 #include <fcntl.h>
37 #include <process.h>
38 #endif
39 
40 #include <iostream>
41 #include <string>
42 #include <sstream>
43 #include <fstream>
44 
45 #include <cstring>
46 #include <ctime>
47 
48 //#define DODS_DEBUG
49 
50 #include <DAS.h>
51 #include <DDS.h>
52 #include <ConstraintEvaluator.h>
53 #include <DDXParserSAX2.h>
54 #include <Ancillary.h>
55 #include <XDRStreamMarshaller.h>
56 #include <XDRFileUnMarshaller.h>
57 
58 #include <DMR.h>
59 #include <D4Group.h>
60 #include <XMLWriter.h>
61 #include <D4AsyncUtil.h>
62 #include <D4StreamMarshaller.h>
63 #include <chunked_ostream.h>
64 #include <chunked_istream.h>
65 #include <D4ConstraintEvaluator.h>
66 #include <D4FunctionEvaluator.h>
67 #include <D4BaseTypeFactory.h>
68 
69 #include <ServerFunctionsList.h>
70 
71 // #include <debug.h>
72 #include <mime_util.h> // for last_modified_time() and rfc_822_date()
73 #include <escaping.h>
74 #include <util.h>
75 #ifndef WIN32
76 #include <SignalHandler.h>
77 #include <EventHandler.h>
78 #include <AlarmHandler.h>
79 #endif
80 
81 #include "TheBESKeys.h"
82 #include "BESDapResponseBuilder.h"
83 #include "BESContextManager.h"
84 #include "BESDapResponseCache.h"
86 #include "BESDebug.h"
87 
88 #define DAP_PROTOCOL_VERSION "3.2"
89 
90 const std::string CRLF = "\r\n"; // Change here, expr-test.cc
91 
92 using namespace std;
93 using namespace libdap;
94 
98 {
99  // Set default values. Don't use the C++ constructor initialization so
100  // that a subclass can have more control over this process.
101  d_dataset = "";
102  d_dap2ce = "";
103  d_btp_func_ce = "";
104  d_timeout = 0;
105 
106  d_default_protocol = DAP_PROTOCOL_VERSION;
107 
108  d_response_cache = 0;
109 
110  d_dap4ce = "";
111  d_dap4function = "";
112  d_store_result = "";
113  d_async_accepted = "";
114 }
115 
119 {
120  if (!d_response_cache) d_response_cache = BESDapResponseCache::get_instance();
121 
122  return d_response_cache;
123 }
124 
126 {
127  if (d_response_cache) delete d_response_cache;
128 
129  // If an alarm was registered, delete it. The register code in SignalHandler
130  // always deletes the old alarm handler object, so only the one returned by
131  // remove_handler needs to be deleted at this point.
132  delete dynamic_cast<AlarmHandler*>(SignalHandler::instance()->remove_handler(SIGALRM));
133 }
134 
142 {
143  return d_dap2ce;
144 }
145 
157 {
158  d_dap2ce = www2id(_ce, "%", "%20");
159 }
160 
165 {
166  return d_dap4ce;
167 }
168 
180 {
181  d_dap4ce = www2id(_ce, "%", "%20");
182 }
183 
188 {
189  return d_dap4function;
190 }
191 
204 {
205  d_dap4function = www2id(_func, "%", "%20");
206 }
207 
209 {
210  return d_store_result;
211 }
212 
214 {
215  d_store_result = _sr;
216  BESDEBUG("dap", "BESDapResponseBuilder::set_store_result() - store_result: " << _sr << endl);
217 }
218 
220 {
221  return d_async_accepted;
222 }
223 
225 {
226  d_async_accepted = _aa;
227  BESDEBUG("dap", "BESDapResponseBuilder::set_async_accepted() - async_accepted: " << _aa << endl);
228 }
229 
239 {
240  return d_dataset;
241 }
242 
254 {
255  d_dataset = www2id(ds, "%", "%20");
256 }
257 
264 {
265  d_timeout = t;
266 }
267 
270 {
271  return d_timeout;
272 }
273 
277 void BESDapResponseBuilder::establish_timeout(ostream &stream) const
278 {
279 #ifndef WIN32
280  if (d_timeout > 0) {
281  SignalHandler *sh = SignalHandler::instance();
282  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
283  delete old_eh;
284  alarm(d_timeout);
285  }
286 #endif
287 }
288 
290 {
291  alarm(0);
292 }
293 
294 static string::size_type find_closing_paren(const string &ce, string::size_type pos)
295 {
296  // Iterate over the string finding all ( or ) characters until the matching ) is found.
297  // For each ( found, increment count. When a ) is found and count is zero, it is the
298  // matching closing paren, otherwise, decrement count and keep looking.
299  int count = 1;
300  do {
301  pos = ce.find_first_of("()", pos + 1);
302  if (pos == string::npos) throw Error(malformed_expr,
303  "Expected to find a matching closing parenthesis in " + ce);
304 
305  if (ce[pos] == '(') ++count;
306  else
307  --count; // must be ')'
308 
309  } while (count > 0);
310 
311  return pos;
312 }
313 
320 void BESDapResponseBuilder::split_ce(ConstraintEvaluator &eval, const string &expr)
321 {
322  BESDEBUG("dap", "Entering ResponseBuilder::split_ce" << endl);
323  string ce;
324  if (!expr.empty()) ce = expr;
325  else
326  ce = d_dap2ce;
327 
328  string btp_function_ce = "";
329  string::size_type pos = 0;
330 
331  // This hack assumes that the functions are listed first. Look for the first
332  // open paren and the last closing paren to accommodate nested function calls
333  string::size_type first_paren = ce.find("(", pos);
334  string::size_type closing_paren = string::npos;
335  if (first_paren != string::npos) closing_paren = find_closing_paren(ce, first_paren); //ce.find(")", pos);
336 
337  while (first_paren != string::npos && closing_paren != string::npos) {
338  // Maybe a BTP function; get the name of the potential function
339  string name = ce.substr(pos, first_paren - pos);
340 
341  // is this a BTP function
342  btp_func f;
343  if (eval.find_function(name, &f)) {
344  // Found a BTP function
345  if (!btp_function_ce.empty()) btp_function_ce += ",";
346  btp_function_ce += ce.substr(pos, closing_paren + 1 - pos);
347  ce.erase(pos, closing_paren + 1 - pos);
348  if (ce[pos] == ',') ce.erase(pos, 1);
349  }
350  else {
351  pos = closing_paren + 1;
352  // exception?
353  if (pos < ce.length() && ce.at(pos) == ',') ++pos;
354  }
355 
356  first_paren = ce.find("(", pos);
357  closing_paren = ce.find(")", pos);
358  }
359 
360  d_dap2ce = ce;
361  d_btp_func_ce = btp_function_ce;
362 
363  BESDEBUG("dap", "Modified constraint: " << d_dap2ce << endl);
364  BESDEBUG("dap", "BTP Function part: " << btp_function_ce << endl);
365 }
366 
381 void BESDapResponseBuilder::send_das(ostream &out, DAS &das, bool with_mime_headers) const
382 {
383  if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
384 
385  das.print(out);
386 
387  out << flush;
388 }
389 
407 void BESDapResponseBuilder::send_das(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
408  bool with_mime_headers)
409 {
410  // Set up the alarm.
411  establish_timeout(out);
412  dds.set_timeout(d_timeout);
413 
414  if (!constrained) {
415  if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
416 
417  dds.print_das(out);
418  out << flush;
419 
420  return;
421  }
422 
423  split_ce(eval);
424 
425  // If there are functions, parse them and eval.
426  // Use that DDS and parse the non-function ce
427  // Serialize using the second ce and the second dds
428  if (!d_btp_func_ce.empty()) {
429  DDS *fdds = 0;
430  string cache_token = "";
431  ConstraintEvaluator func_eval;
432 
433  if (responseCache()) {
434  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &func_eval, cache_token);
435  }
436  else {
437  func_eval.parse_constraint(d_btp_func_ce, dds);
438  fdds = func_eval.eval_function_clauses(dds);
439  }
440 
441  if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset),
442  dds.get_dap_version());
443 
444  fdds->print_das(out);
445 
446  if (responseCache()) responseCache()->unlock_and_close(cache_token);
447 
448  delete fdds;
449  }
450  else {
451  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
452 
453  if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset),
454  dds.get_dap_version());
455 
456  dds.print_das(out);
457  }
458 
459  out << flush;
460 }
461 
480 void BESDapResponseBuilder::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
481  bool with_mime_headers)
482 {
483  if (!constrained) {
484  if (with_mime_headers) set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset),
485  dds.get_dap_version());
486 
487  dds.print(out);
488  out << flush;
489  return;
490  }
491 
492  // Set up the alarm.
493  establish_timeout(out);
494  dds.set_timeout(d_timeout);
495 
496  // Split constraint into two halves
497  split_ce(eval);
498 
499  // If there are functions, parse them and eval.
500  // Use that DDS and parse the non-function ce
501  // Serialize using the second ce and the second dds
502  if (!d_btp_func_ce.empty()) {
503  string cache_token = "";
504  DDS *fdds = 0;
505  ConstraintEvaluator func_eval;
506 
507  if (responseCache()) {
508  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &func_eval, cache_token);
509  }
510  else {
511  func_eval.parse_constraint(d_btp_func_ce, dds);
512  fdds = func_eval.eval_function_clauses(dds);
513  }
514 
515  // Server functions might mark variables to use their read()
516  // methods. Clear that so the CE in d_dap2ce will control what is
517  // sent. If that is empty (there was only a function call) all
518  // of the variables in the intermediate DDS (i.e., the function
519  // result) will be sent.
520  fdds->mark_all(false);
521 
522  eval.parse_constraint(d_dap2ce, *fdds);
523 
524  if (with_mime_headers) set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset),
525  dds.get_dap_version());
526 
527  fdds->print_constrained(out);
528 
529  if (responseCache()) responseCache()->unlock_and_close(cache_token);
530 
531  delete fdds;
532  }
533  else {
534  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
535 
536  if (with_mime_headers) set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset),
537  dds.get_dap_version());
538 
539  dds.print_constrained(out);
540  }
541 
542  out << flush;
543 }
544 
559 bool BESDapResponseBuilder::store_dap2_result(ostream &out, DDS &dds, ConstraintEvaluator &eval)
560 {
561  if (get_store_result().empty()) return false;
562 
563  string serviceUrl = get_store_result();
564 
565  XMLWriter xmlWrtr;
566  D4AsyncUtil d4au;
567 
568  bool found;
569  string *stylesheet_ref = 0, ss_ref_value;
570  TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
571  if (found && ss_ref_value.length() > 0) {
572  stylesheet_ref = &ss_ref_value;
573  }
574 
576  if (resultCache == NULL) {
577 
583  string msg = "The Stored Result request cannot be serviced. ";
584  msg += "Unable to acquire StoredResultCache instance. ";
585  msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
586 
587  BESDEBUG("dap", "[WARNING] " << msg << endl);
588 
589  d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
590  out << xmlWrtr.get_doc();
591  out << flush;
592 
593  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - Sent AsyncRequestRejected" << endl);
594  }
595  else if (get_async_accepted().length() != 0) {
596 
600  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - serviceUrl="<< serviceUrl << endl);
601 
603  string storedResultId = "";
604  storedResultId = resultCache->store_dap2_result(dds, get_ce(), this, &eval);
605 
606  BESDEBUG("dap",
607  "BESDapResponseBuilder::store_dap2_result() - storedResultId='"<< storedResultId << "'" << endl);
608 
609  string targetURL = resultCache->assemblePath(serviceUrl, storedResultId);
610  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - targetURL='"<< targetURL << "'" << endl);
611 
612  XMLWriter xmlWrtr;
613  d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
614  out << xmlWrtr.get_doc();
615  out << flush;
616 
617  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - sent DAP4 AsyncAccepted response" << endl);
618  }
619  else {
624  d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
625  out << xmlWrtr.get_doc();
626  out << flush;
627 
628  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - sent DAP4 AsyncRequired response" << endl);
629  }
630 
631  return true;
632 
633 }
634 
635 #if 0
636 // the original version. jhrg 3/9/15
637 bool BESDapResponseBuilder::store_dap2_result(ostream &out, DDS &dds, ConstraintEvaluator &eval)
638 {
639 
640  if (get_store_result().length() != 0) { // use !empty()
641  string serviceUrl = get_store_result();
642 
643  XMLWriter xmlWrtr;
644  D4AsyncUtil d4au;
645 
646  bool found;
647  string *stylesheet_ref = 0, ss_ref_value;
648  TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
649  if (found && ss_ref_value.length() > 0) {
650  stylesheet_ref = &ss_ref_value;
651  }
652 
654  if (resultCache == NULL) {
655 
661  string msg = "The Stored Result request cannot be serviced. ";
662  msg += "Unable to acquire StoredResultCache instance. ";
663  msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
664 
665  BESDEBUG("dap", "[WARNING] " << msg << endl);
666  d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
667  out << xmlWrtr.get_doc();
668  out << flush;
669  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - Sent AsyncRequestRejected" << endl);
670  return true;
671  }
672 
673  if (get_async_accepted().length() != 0) {
674 
678  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - serviceUrl="<< serviceUrl << endl);
679 
681  string storedResultId = "";
682  storedResultId = resultCache->store_dap2_result(dds, get_ce(), this, &eval);
683 
684  BESDEBUG("dap",
685  "BESDapResponseBuilder::store_dap2_result() - storedResultId='"<< storedResultId << "'" << endl);
686 
687  string targetURL = resultCache->assemblePath(serviceUrl, storedResultId);
688  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - targetURL='"<< targetURL << "'" << endl);
689 
690  XMLWriter xmlWrtr;
691  d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
692  out << xmlWrtr.get_doc();
693  out << flush;
694  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - sent DAP4 AsyncAccepted response" << endl);
695 
696  }
697  else {
702  d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
703  out << xmlWrtr.get_doc();
704  out << flush;
705  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - sent DAP4 AsyncRequired response" << endl);
706  }
707 
708  return true;
709 
710  }
711  return false;
712 }
713 #endif
714 
718 void BESDapResponseBuilder::serialize_dap2_data_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool ce_eval)
719 {
720  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap2_data_dds() - BEGIN" << endl);
721 
722  dds.print_constrained(out);
723  out << "Data:\n";
724  out << flush;
725 
726  XDRStreamMarshaller m(out);
727 
728  // Send all variables in the current projection (send_p())
729  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
730  if ((*i)->send_p()) {
731  (*i)->serialize(eval, dds, m, ce_eval);
732  }
733  }
734 
735  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap2_data_dds() - END" << endl);
736 }
737 
742 void BESDapResponseBuilder::serialize_dap2_data_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval,
743  const string &boundary, const string &start, bool ce_eval)
744 {
745  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap2_data_ddx() - BEGIN" << endl);
746 
747  // Write the MPM headers for the DDX (text/xml) part of the response
748  libdap::set_mime_ddx_boundary(out, boundary, start, dods_ddx, x_plain);
749 
750  // Make cid
751  uuid_t uu;
752  uuid_generate(uu);
753  char uuid[37];
754  uuid_unparse(uu, &uuid[0]);
755  char domain[256];
756  if (getdomainname(domain, 255) != 0 || strlen(domain) == 0) strncpy(domain, "opendap.org", 255);
757 
758  string cid = string(&uuid[0]) + "@" + string(&domain[0]);
759  // Send constrained DDX with a data blob reference
760  dds.print_xml_writer(out, true, cid);
761 
762  // write the data part mime headers here
763  set_mime_data_boundary(out, boundary, cid, dods_data_ddx /* old value dap4_data*/, x_plain);
764 
765  XDRStreamMarshaller m(out);
766 
767  // Send all variables in the current projection (send_p()). In DAP4,
768  // all of the top-level variables are serialized with their checksums.
769  // Internal variables are not.
770  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
771  if ((*i)->send_p()) {
772  (*i)->serialize(eval, dds, m, ce_eval);
773  }
774  }
775 
776  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap2_data_ddx() - END" << endl);
777 }
778 
795 void BESDapResponseBuilder::send_dap2_data(ostream &data_stream, DDS &dds, ConstraintEvaluator &eval,
796  bool with_mime_headers)
797 {
798  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - BEGIN"<< endl);
799 
800  // Set up the alarm.
801  establish_timeout(data_stream);
802  dds.set_timeout(d_timeout);
803 
804  // Split constraint into two halves
805  split_ce(eval);
806 
807  // If there are functions, parse them and eval.
808  // Use that DDS and parse the non-function ce
809  // Serialize using the second ce and the second dds
810  if (!get_btp_func_ce().empty()) {
811  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - Found function(s) in CE: " << get_btp_func_ce() << endl);
812 
813  string cache_token = "";
814  DDS *fdds = 0;
815  // Define a local ce evaluator so that the clause from the function parse
816  // won't get treated like selection clauses later on when serialize is called
817  // on the DDS (fdds)
818  ConstraintEvaluator func_eval;
819  if (responseCache()) {
820  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - Using the cache for the server function CE" << endl);
821  fdds = responseCache()->cache_dataset(dds, get_btp_func_ce(), this, &func_eval, cache_token);
822  }
823  else {
824  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - Cache not found; (re)calculating" << endl);
825  func_eval.parse_constraint(get_btp_func_ce(), dds);
826  fdds = func_eval.eval_function_clauses(dds);
827  }
828 
829  // Server functions might mark variables to use their read()
830  // methods. Clear that so the CE in d_dap2ce will control what is
831  // sent. If that is empty (there was only a function call) all
832  // of the variables in the intermediate DDS (i.e., the function
833  // result) will be sent.
834  fdds->mark_all(false);
835 
836  eval.parse_constraint(get_ce(), *fdds);
837 
838  fdds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
839 
840  if (fdds->get_response_limit() != 0 && fdds->get_request_size(true) > fdds->get_response_limit()) {
841  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
842  + "KB is too large; requests for this user are limited to "
843  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
844  throw Error(msg);
845  }
846 
847  if (with_mime_headers) set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset),
848  dds.get_dap_version());
849 
850  if (!store_dap2_result(data_stream, dds, eval)) {
851  serialize_dap2_data_dds(data_stream, *fdds, eval, true /* was 'false'. jhrg 3/10/15 */);
852  }
853 
854  if (responseCache()) responseCache()->unlock_and_close(cache_token);
855 
856  delete fdds;
857  }
858  else {
859  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - Simple constraint" << endl);
860 
861  eval.parse_constraint(get_ce(), dds); // Throws Error if the ce doesn't parse.
862 
863  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
864 
865  if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
866  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
867  + "KB is too large; requests for this user are limited to "
868  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
869  throw Error(msg);
870  }
871 
872  if (with_mime_headers) set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset),
873  dds.get_dap_version());
874 
875  if (!store_dap2_result(data_stream, dds, eval)) {
876  serialize_dap2_data_dds(data_stream, dds, eval);
877  }
878  }
879 
880  data_stream << flush;
881 
882  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - END"<< endl);
883 
884 }
885 
899 void BESDapResponseBuilder::send_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers)
900 {
901  if (d_dap2ce.empty()) {
902  if (with_mime_headers) set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset),
903  dds.get_dap_version());
904 
905  dds.print_xml_writer(out, false /*constrained */, "");
906  //dds.print(out);
907  out << flush;
908  return;
909  }
910 
911  // Set up the alarm.
912  establish_timeout(out);
913  dds.set_timeout(d_timeout);
914 
915  // Split constraint into two halves
916  split_ce(eval);
917 
918  // If there are functions, parse them and eval.
919  // Use that DDS and parse the non-function ce
920  // Serialize using the second ce and the second dds
921  if (!d_btp_func_ce.empty()) {
922  string cache_token = "";
923  DDS *fdds = 0;
924  ConstraintEvaluator func_eval;
925 
926  if (responseCache()) {
927  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &func_eval, cache_token);
928  }
929  else {
930  func_eval.parse_constraint(d_btp_func_ce, dds);
931  fdds = func_eval.eval_function_clauses(dds);
932  }
933 
934  // Server functions might mark variables to use their read()
935  // methods. Clear that so the CE in d_dap2ce will control what is
936  // sent. If that is empty (there was only a function call) all
937  // of the variables in the intermediate DDS (i.e., the function
938  // result) will be sent.
939  fdds->mark_all(false);
940 
941  eval.parse_constraint(d_dap2ce, *fdds);
942 
943  if (with_mime_headers) set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset),
944  dds.get_dap_version());
945 
946  fdds->print_constrained(out);
947 
948  if (responseCache()) responseCache()->unlock_and_close(cache_token);
949 
950  delete fdds;
951  }
952  else {
953  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
954 
955  if (with_mime_headers) set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset),
956  dds.get_dap_version());
957 
958  // dds.print_constrained(out);
959  dds.print_xml_writer(out, true, "");
960  }
961 
962  out << flush;
963 }
964 
965 void BESDapResponseBuilder::send_dmr(ostream &out, DMR &dmr, bool with_mime_headers)
966 {
967  // If the CE is not empty, parse it. The projections, etc., are set as a side effect.
968  // If the parser returns false, the expression did not parse. The parser may also
969  // throw Error
970  if (!d_dap4ce.empty()) {
971 
972  BESDEBUG("dap", "BESDapResponseBuilder::send_dmr() - Parsing DAP4 constraint: '"<< d_dap4ce << "'"<< endl);
973 
974  D4ConstraintEvaluator parser(&dmr);
975  bool parse_ok = parser.parse(d_dap4ce);
976  if (!parse_ok) throw Error("Constraint Expression failed to parse.");
977  }
978  // with an empty CE, send everything. Even though print_dap4() and serialize()
979  // don't need this, other code may depend on send_p being set. This may change
980  // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
981  else {
982  dmr.root()->set_send_p(true);
983  }
984 
985  if (with_mime_headers) set_mime_text(out, dap4_dmr, x_plain, last_modified_time(d_dataset), dmr.dap_version());
986 
987  XMLWriter xml;
988  dmr.print_dap4(xml, /*constrained &&*/!d_dap4ce.empty() /* true == constrained */);
989  out << xml.get_doc() << flush;
990 
991  out << flush;
992 }
993 
994 void BESDapResponseBuilder::send_dap4_data_using_ce(ostream &out, DMR &dmr, bool with_mime_headers)
995 {
996  if (!d_dap4ce.empty()) {
997  D4ConstraintEvaluator parser(&dmr);
998  bool parse_ok = parser.parse(d_dap4ce);
999  if (!parse_ok) throw Error("Constraint Expression (" + d_dap4ce + ") failed to parse.");
1000  }
1001  // with an empty CE, send everything. Even though print_dap4() and serialize()
1002  // don't need this, other code may depend on send_p being set. This may change
1003  // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
1004  else {
1005  dmr.root()->set_send_p(true);
1006  }
1007 
1008  if (dmr.response_limit() != 0 && dmr.request_size(true) > dmr.response_limit()) {
1009  string msg = "The Request for " + long_to_string(dmr.request_size(true) / 1024)
1010  + "MB is too large; requests for this user are limited to "
1011  + long_to_string(dmr.response_limit() / 1024) + "MB.";
1012  throw Error(msg);
1013  }
1014 
1015  if (!store_dap4_result(out, dmr)) {
1016  serialize_dap4_data(out, dmr, with_mime_headers);
1017  }
1018 }
1019 
1020 void BESDapResponseBuilder::send_dap4_data(ostream &out, DMR &dmr, bool with_mime_headers)
1021 {
1022  try {
1023  // Set up the alarm.
1024  establish_timeout(out);
1025 
1026  // If a function was passed in with this request, evaluate it and use that DMR
1027  // for the remainder of this request.
1028  // TODO Add caching for these function invocations
1029  if (!d_dap4function.empty()) {
1030  D4BaseTypeFactory d4_factory;
1031  DMR function_result(&d4_factory, "function_results");
1032 
1033  // Function modules load their functions onto this list. The list is
1034  // part of libdap, not the BES.
1035  if (!ServerFunctionsList::TheList()) throw Error(
1036  "The function expression could not be evaluated because there are no server functions defined on this server");
1037 
1038  D4FunctionEvaluator parser(&dmr, ServerFunctionsList::TheList());
1039  bool parse_ok = parser.parse(d_dap4function);
1040  if (!parse_ok) throw Error("Function Expression (" + d_dap4function + ") failed to parse.");
1041 
1042  parser.eval(&function_result);
1043 
1044  // Now use the results of running the functions for the remainder of the
1045  // send_data operation.
1046  send_dap4_data_using_ce(out, function_result, with_mime_headers);
1047  }
1048 
1049  send_dap4_data_using_ce(out, dmr, with_mime_headers);
1050 
1051  remove_timeout();
1052  }
1053  catch (...) {
1054  remove_timeout();
1055  throw;
1056  }
1057 }
1058 
1062 void BESDapResponseBuilder::serialize_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers)
1063 {
1064  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap4_data() - BEGIN" << endl);
1065 
1066  if (with_mime_headers) set_mime_binary(out, dap4_data, x_plain, last_modified_time(d_dataset), dmr.dap_version());
1067 
1068  // Write the DMR
1069  XMLWriter xml;
1070  dmr.print_dap4(xml, !d_dap4ce.empty());
1071 
1072  // now make the chunked output stream; set the size to be at least chunk_size
1073  // but make sure that the whole of the xml plus the CRLF can fit in the first
1074  // chunk. (+2 for the CRLF bytes).
1075  chunked_ostream cos(out, max((unsigned int) CHUNK_SIZE, xml.get_doc_size() + 2));
1076 
1077  // using flush means that the DMR and CRLF are in the first chunk.
1078  cos << xml.get_doc() << CRLF << flush;
1079 
1080  // Write the data, chunked with checksums
1081  D4StreamMarshaller m(cos);
1082  dmr.root()->serialize(m, dmr, !d_dap4ce.empty());
1083 
1084  out << flush;
1085 
1086  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap4_data() - END" << endl);
1087 }
1088 
1103 bool BESDapResponseBuilder::store_dap4_result(ostream &out, libdap::DMR &dmr)
1104 {
1105  if (get_store_result().length() != 0) {
1106  string serviceUrl = get_store_result();
1107 
1108  D4AsyncUtil d4au;
1109  XMLWriter xmlWrtr;
1110 
1111  bool found;
1112  string *stylesheet_ref = 0, ss_ref_value;
1113  TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
1114  if (found && ss_ref_value.length() > 0) {
1115  stylesheet_ref = &ss_ref_value;
1116  }
1117 
1119  if (resultCache == NULL) {
1120 
1126  string msg = "The Stored Result request cannot be serviced. ";
1127  msg += "Unable to acquire StoredResultCache instance. ";
1128  msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
1129 
1130  BESDEBUG("dap", "[WARNING] " << msg << endl);
1131  d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
1132  out << xmlWrtr.get_doc();
1133  out << flush;
1134  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - Sent AsyncRequestRejected" << endl);
1135 
1136  return true;
1137  }
1138 
1139  if (get_async_accepted().length() != 0) {
1140 
1144  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - serviceUrl="<< serviceUrl << endl);
1145 
1146  string storedResultId = "";
1147  storedResultId = resultCache->store_dap4_result(dmr, get_ce(), this);
1148 
1149  BESDEBUG("dap",
1150  "BESDapResponseBuilder::store_dap4_result() - storedResultId='"<< storedResultId << "'" << endl);
1151 
1152  string targetURL = resultCache->assemblePath(serviceUrl, storedResultId);
1153  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - targetURL='"<< targetURL << "'" << endl);
1154 
1155  d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
1156  out << xmlWrtr.get_doc();
1157  out << flush;
1158  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - sent AsyncAccepted" << endl);
1159 
1160  }
1161  else {
1166  d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
1167  out << xmlWrtr.get_doc();
1168  out << flush;
1169  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - sent AsyncAccepted" << endl);
1170  }
1171 
1172  return true;
1173  }
1174 
1175  return false;
1176 }
1177 
virtual void send_dmr(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers=true)
virtual std::string get_ce() const
Return the entire DAP2 constraint expression in a string.
virtual std::string get_dap4ce() const
Return the entire DAP4 constraint expression in a string.
const std::string CRLF
static string assemblePath(const string &firstPart, const string &secondPart, bool addLeadingSlash=false)
virtual void serialize_dap2_data_dds(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool ce_eval=true)
Build/return the BLOB part of the DAP2 data response.
static BESDapResponseCache * get_instance()
Get the default instance of the BESDapResponseCache object.
STL namespace.
This class is used to cache DAP2 response objects.
virtual void send_dap2_data(std::ostream &data_stream, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
Send the data in the DDS object back to the client program.
virtual void send_das(std::ostream &out, libdap::DAS &das, bool with_mime_headers=true) const
virtual string store_dap4_result(libdap::DMR &dmr, const string &constraint, BESDapResponseBuilder *rb)
virtual std::string get_dap4function() const
Return the entire DAP4 server side function expression in a string.
static class NCMLUtil overview
virtual BESDapResponseCache * responseCache()
Lazy getter for the ResponseCache.
void initialize()
Called when initializing a ResponseBuilder that's not going to be passed command line arguments...
static BESStoredDapResultCache * get_instance()
Get the default instance of the BESStoredDapResultCache object.
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...
#define NULL
Definition: wcsUtil.h:65
virtual std::string get_store_result() const
virtual void remove_timeout() const
virtual void set_async_accepted(std::string _aa)
#define DAP_PROTOCOL_VERSION
void set_timeout(int timeout=0)
Set the server's timeout value.
virtual void set_dataset_name(const std::string _dataset)
Set the dataset name, which is a string used to access the dataset on the machine running the server...
virtual void set_dap4function(std::string _func)
Set the DAP4 Server Side Fucntion expression.
virtual void send_ddx(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
Send the DDX response.
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:453
virtual void send_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers=true)
virtual void serialize_dap2_data_ddx(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, const std::string &boundary, const std::string &start, bool ce_eval=true)
Serialize a DAP3.2 DataDDX to the stream "out".
int get_timeout() const
Get the server's timeout value.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
virtual std::string get_async_accepted() const
virtual void set_ce(std::string _ce)
Set the DAP2 constraint expression.
bool store_dap2_result(ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval)
Should this result be returned using the asynchronous response mechanism? Look at the 'store_result' ...
#define max(a, b)
Definition: os_utils.h:67
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:48
virtual void establish_timeout(std::ostream &stream) const
Use values of this instance to establish a timeout alarm for the server.
virtual bool store_dap4_result(ostream &out, libdap::DMR &dmr)
Should this DAP4 result be stored and the client sent an Asynchronous response? This code looks at th...
virtual std::string get_dataset_name() const
The ``dataset name'' is the filename or other string that the filter program will use to access the d...
virtual void set_dap4ce(std::string _ce)
Set the DAP4 constraint expression.
This class is used to cache DAP2 response objects.
virtual void set_store_result(std::string _sr)
virtual string store_dap2_result(libdap::DDS &dds, const std::string &constraint, BESDapResponseBuilder *rb, libdap::ConstraintEvaluator *eval)
virtual void serialize_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers=true)
Serialize the DAP4 data response to the passed stream.
virtual void send_dds(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool constrained=false, bool with_mime_headers=true)
This function formats and prints an ASCII representation of a DDS on stdout.
void send_dap4_data_using_ce(std::ostream &out, libdap::DMR &dmr, bool with_mime_headersr)