bes  Updated for version 3.17.1
BESDapResponseBuilder.cc
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 
31 #ifdef HAVE_UUID_UUID_H
32 #include <uuid/uuid.h> // used to build CID header value for data ddx
33 #elif defined(HAVE_UUID_H)
34 #include <uuid.h>
35 #else
36 #error "Could not find UUID library header"
37 #endif
38 
39 
40 #ifndef WIN32
41 #include <sys/wait.h>
42 #else
43 #include <io.h>
44 #include <fcntl.h>
45 #include <process.h>
46 #endif
47 
48 #include <iostream>
49 #include <string>
50 #include <sstream>
51 #include <fstream>
52 
53 #include <cstring>
54 #include <ctime>
55 
56 //#define DODS_DEBUG
57 #define CLEAR_LOCAL_DATA
58 #undef FUNCTION_CACHING
59 #undef USE_LOCAL_TIMEOUT_SCHEME
60 
61 #include <DAS.h>
62 #include <DDS.h>
63 #include <Structure.h>
64 #include <ConstraintEvaluator.h>
65 #include <DDXParserSAX2.h>
66 #include <Ancillary.h>
67 #include <XDRStreamMarshaller.h>
68 #include <XDRFileUnMarshaller.h>
69 
70 #include <DMR.h>
71 #include <D4Group.h>
72 #include <XMLWriter.h>
73 #include <D4AsyncUtil.h>
74 #include <D4StreamMarshaller.h>
75 #include <chunked_ostream.h>
76 #include <chunked_istream.h>
77 #include <D4ConstraintEvaluator.h>
78 #include <D4FunctionEvaluator.h>
79 #include <D4BaseTypeFactory.h>
80 
81 #include <ServerFunctionsList.h>
82 
83 #include <mime_util.h> // for last_modified_time() and rfc_822_date()
84 #include <escaping.h>
85 #include <util.h>
86 #if USE_LOCAL_TIMEOUT_SCHEME
87 #ifndef WIN32
88 #include <SignalHandler.h>
89 #include <EventHandler.h>
90 #include <AlarmHandler.h>
91 #endif
92 #endif
93 
94 #include "TheBESKeys.h"
95 #include "BESDapResponseBuilder.h"
96 #include "BESContextManager.h"
97 #include "BESDapResponseCache.h"
98 #include "BESStoredDapResultCache.h"
99 #include "BESUtil.h"
100 #include "BESDebug.h"
101 #include "BESStopWatch.h"
102 #include "DapFunctionUtils.h"
103 
104 using namespace std;
105 using namespace libdap;
106 
107 const string CRLF = "\r\n"; // Change here, expr-test.cc
108 const string BES_KEY_TIMEOUT_CANCEL = "BES.CancelTimeoutOnSend";
109 
115 {
116  bool found = false;
117  string cancel_timeout_on_send = "";
118  TheBESKeys::TheKeys()->get_value(BES_KEY_TIMEOUT_CANCEL, cancel_timeout_on_send, found);
119  if (found && !cancel_timeout_on_send.empty()) {
120  // The default value is false.
121  downcase(cancel_timeout_on_send);
122  if (cancel_timeout_on_send == "yes" || cancel_timeout_on_send == "true")
123  d_cancel_timeout_on_send = true;
124  }
125 }
126 
127 BESDapResponseBuilder::~BESDapResponseBuilder()
128 {
129 #if USE_LOCAL_TIMEOUT_SCHEME
130  // If an alarm was registered, delete it. The register code in SignalHandler
131  // always deletes the old alarm handler object, so only the one returned by
132  // remove_handler needs to be deleted at this point.
133  delete dynamic_cast<AlarmHandler*>(SignalHandler::instance()->remove_handler(SIGALRM));
134 #endif
135 }
136 
144 {
145  return d_dap2ce;
146 }
147 
159 {
160  d_dap2ce = www2id(_ce, "%", "%20");
161 }
162 
167 {
168  return d_dap4ce;
169 }
170 
182 {
183  d_dap4ce = www2id(_ce, "%", "%20");
184 }
185 
190 {
191  return d_dap4function;
192 }
193 
206 {
207  d_dap4function = www2id(_func, "%", "%20");
208 }
209 
210 std::string BESDapResponseBuilder::get_store_result() const
211 {
212  return d_store_result;
213 }
214 
215 void BESDapResponseBuilder::set_store_result(std::string _sr)
216 {
217  d_store_result = _sr;
218  BESDEBUG("dap", "BESDapResponseBuilder::set_store_result() - store_result: " << _sr << endl);
219 }
220 
221 std::string BESDapResponseBuilder::get_async_accepted() const
222 {
223  return d_async_accepted;
224 }
225 
226 void BESDapResponseBuilder::set_async_accepted(std::string _aa)
227 {
228  d_async_accepted = _aa;
229  BESDEBUG("dap", "BESDapResponseBuilder::set_async_accepted() - async_accepted: " << _aa << endl);
230 }
231 
241 {
242  return d_dataset;
243 }
244 
256 {
257  d_dataset = www2id(ds, "%", "%20");
258 }
259 
266 {
267  d_timeout = t;
268 }
269 
272 {
273  return d_timeout;
274 }
275 
282 void
284 {
285 #if USE_LOCAL_TIMEOUT_SCHEME
286 #ifndef WIN32
287  alarm(d_timeout);
288 #endif
289 #endif
290 }
291 
297 void
299 {
300 #if USE_LOCAL_TIMEOUT_SCHEME
301 #ifndef WIN32
302  alarm(0);
303 #endif
304 #endif
305 }
306 
322 {
323  if (d_cancel_timeout_on_send)
324  alarm(0);
325 }
326 
336 {
337 #if USE_LOCAL_TIMEOUT_SCHEME
338 #ifndef WIN32
339  SignalHandler *sh = SignalHandler::instance();
340  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler());
341  delete old_eh;
342 #endif
343 #endif
344 }
345 
346 
353 {
354 #if USE_LOCAL_TIMEOUT_SCHEME
355 #ifndef WIN32
356  if (d_timeout > 0) {
357  SignalHandler *sh = SignalHandler::instance();
358  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler());
359  delete old_eh;
360  alarm(d_timeout);
361  }
362 #endif
363 #endif
364 }
365 
370 {
371 #if USE_LOCAL_TIMEOUT_SCHEME
372  alarm(0);
373 #endif
374 }
375 
376 static string::size_type find_closing_paren(const string &ce, string::size_type pos)
377 {
378  // Iterate over the string finding all ( or ) characters until the matching ) is found.
379  // For each ( found, increment count. When a ) is found and count is zero, it is the
380  // matching closing paren, otherwise, decrement count and keep looking.
381  int count = 1;
382  do {
383  pos = ce.find_first_of("()", pos + 1);
384  if (pos == string::npos)
385  throw Error(malformed_expr, "Expected to find a matching closing parenthesis in " + ce);
386 
387  if (ce[pos] == '(')
388  ++count;
389  else
390  --count; // must be ')'
391 
392  } while (count > 0);
393 
394  return pos;
395 }
396 
403 void BESDapResponseBuilder::split_ce(ConstraintEvaluator &eval, const string &expr)
404 {
405  BESDEBUG("dap", "BESDapResponseBuilder::split_ce() - source expression: " << expr << endl);
406 
407  string ce;
408  if (!expr.empty())
409  ce = expr;
410  else
411  ce = d_dap2ce;
412 
413  BESDEBUG("dap", "BESDapResponseBuilder::split_ce() - ce: " << ce << endl);
414 
415  string btp_function_ce = "";
416  string::size_type pos = 0;
417 
418  // This hack assumes that the functions are listed first. Look for the first
419  // open paren and the last closing paren to accommodate nested function calls
420  string::size_type first_paren = ce.find("(", pos);
421  string::size_type closing_paren = string::npos;
422  if (first_paren != string::npos) closing_paren = find_closing_paren(ce, first_paren); //ce.find(")", pos);
423 
424  while (first_paren != string::npos && closing_paren != string::npos) {
425  // Maybe a BTP function; get the name of the potential function
426  string name = ce.substr(pos, first_paren - pos);
427 
428  // is this a BTP function
429  btp_func f;
430  if (eval.find_function(name, &f)) {
431  // Found a BTP function
432  if (!btp_function_ce.empty()) btp_function_ce += ",";
433  btp_function_ce += ce.substr(pos, closing_paren + 1 - pos);
434  ce.erase(pos, closing_paren + 1 - pos);
435  if (ce[pos] == ',') ce.erase(pos, 1);
436  }
437  else {
438  pos = closing_paren + 1;
439  // exception?
440  if (pos < ce.length() && ce.at(pos) == ',') ++pos;
441  }
442 
443  first_paren = ce.find("(", pos);
444  closing_paren = ce.find(")", pos);
445  }
446 
447  d_dap2ce = ce;
448  d_btp_func_ce = btp_function_ce;
449 
450  BESDEBUG("dap", "BESDapResponseBuilder::split_ce() - Modified constraint: " << d_dap2ce << endl);
451  BESDEBUG("dap", "BESDapResponseBuilder::split_ce() - BTP Function part: " << btp_function_ce << endl);
452  BESDEBUG("dap", "BESDapResponseBuilder::split_ce() - END" << endl);
453 }
454 
469 void BESDapResponseBuilder::send_das(ostream &out, DAS &das, bool with_mime_headers) const
470 {
471  if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
472 
473  das.print(out);
474 
475  out << flush;
476 }
477 
495 void BESDapResponseBuilder::send_das(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
496  bool with_mime_headers)
497 {
498 #if USE_LOCAL_TIMEOUT_SCHEME
499  // Set up the alarm.
500  establish_timeout(out);
501  dds.set_timeout(d_timeout);
502 #endif
503  if (!constrained) {
504  if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
505 
506  conditional_timeout_cancel();
507 
508  dds.print_das(out);
509  out << flush;
510 
511  return;
512  }
513 
514  split_ce(eval);
515 
516  // If there are functions, parse them and eval.
517  // Use that DDS and parse the non-function ce
518  // Serialize using the second ce and the second dds
519  if (!d_btp_func_ce.empty()) {
520  DDS *fdds = 0;
521  string cache_token = "";
522  ConstraintEvaluator func_eval;
524 
525  if (responseCache) {
526  fdds = responseCache->cache_dataset(dds, d_btp_func_ce, this, &func_eval, cache_token);
527  }
528  else {
529  func_eval.parse_constraint(d_btp_func_ce, dds);
530  fdds = func_eval.eval_function_clauses(dds);
531  }
532 
533  if (with_mime_headers)
534  set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
535 
536  conditional_timeout_cancel();
537 
538 
539  fdds->print_das(out);
540 
541  if (responseCache)
542  responseCache->unlock_and_close(cache_token);
543 
544  delete fdds;
545  }
546  else {
547  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
548 
549  if (with_mime_headers)
550  set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
551 
552  conditional_timeout_cancel();
553 
554 
555  dds.print_das(out);
556  }
557 
558  out << flush;
559 }
560 
561 
562 #if 0
563 
567 static bool ends_with (const string &full_string, const string &ending) {
568  if (full_string.length() >= ending.length()) {
569  return (0 == full_string.compare (full_string.length() - ending.length(), ending.length(), ending));
570  } else {
571  return false;
572  }
573 }
574 
603 static DDS *promote_function_output_structure(DDS *fdds)
604 {
605  // Look in the top level of the DDS for a promotable member - i.e. a member
606  // variable that is a collection and whose name ends with "_unwrap"
607  bool found_promotable_member = false;
608  for (DDS::Vars_citer di = fdds->var_begin(), de = fdds->var_end(); di != de && !found_promotable_member; ++di) {
609  Structure *collection = dynamic_cast<Structure *>(*di);
610  if (collection && ends_with(collection->name(), "_unwrap")) {
611  found_promotable_member = true;
612  }
613  }
614 
615  // If we found one or more promotable member variables, promote them.
616  if (found_promotable_member) {
617 
618  // Dump pointers to the values here temporarily... If we had methods in libdap
619  // that could be used to access the underlying erase() and insert() methods, we
620  // could skip the (maybe expensive) copy operations I use below. What we would
621  // need are ways to delete a Structure/Constructor without calling delete on its
622  // fields and ways to call vector::erase() and vector::insert(). Some of this
623  // exists, but it's not quite enough.
624 
625  DDS *temp_dds = new DDS(fdds->get_factory(), fdds->get_dataset_name(), fdds->get_dap_version());
626 
627  for (DDS::Vars_citer di = fdds->var_begin(), de = fdds->var_end(); di != de; ++di) {
628  Structure *collection = dynamic_cast<Structure *>(*di);
629  if (collection && ends_with(collection->name(), "_unwrap")) {
630  // So we're going to 'flatten this structure' and return its fields
631  Structure::Vars_iter vi;
632  for (vi =collection->var_begin(); vi != collection->var_end(); ++vi) {
633  temp_dds->add_var(*vi); // better to use add_var_nocopy(*vi); need to modify libdap?
634  }
635  }
636  else {
637  temp_dds->add_var(*di);
638  }
639  }
640 
641  delete fdds;
642  return temp_dds;
643  }
644  else {
645  // Otherwise do nothing to alter the DDS
646  return fdds;
647  }
648 }
649 #endif
650 
669 void BESDapResponseBuilder::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
670  bool with_mime_headers)
671 {
672  if (!constrained) {
673  if (with_mime_headers)
674  set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
675 
676  conditional_timeout_cancel();
677 
678 
679  dds.print(out);
680  out << flush;
681  return;
682  }
683 
684 #if USE_LOCAL_TIMEOUT_SCHEME
685  // Set up the alarm.
686  establish_timeout(out);
687  dds.set_timeout(d_timeout);
688 #endif
689 
690  // Split constraint into two halves
691  split_ce(eval);
692 
693  // If there are functions, parse them and eval.
694  // Use that DDS and parse the non-function ce
695  // Serialize using the second ce and the second dds
696  if (!d_btp_func_ce.empty()) {
697  string cache_token = "";
698  DDS *fdds = 0;
699  ConstraintEvaluator func_eval;
700 
702 
703  if (responseCache) {
704  fdds = responseCache->cache_dataset(dds, d_btp_func_ce, this, &func_eval, cache_token);
705  }
706  else {
707  func_eval.parse_constraint(d_btp_func_ce, dds);
708  fdds = func_eval.eval_function_clauses(dds);
709  }
710 
711  // Server functions might mark variables to use their read()
712  // methods. Clear that so the CE in d_dap2ce will control what is
713  // sent. If that is empty (there was only a function call) all
714  // of the variables in the intermediate DDS (i.e., the function
715  // result) will be sent.
716  fdds->mark_all(false);
717 
718  eval.parse_constraint(d_dap2ce, *fdds);
719 
720  if (with_mime_headers)
721  set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
722 
723  // This next step utilizes a well known static method (so really it's a function;),
724  // promote_function_output_structures() to look for
725  // one or more top level Structures whose name indicates (by way of ending with
726  // "_uwrap") that their contents should be promoted (aka moved) to the top level.
727  // This is in support of a hack around the current API where server side functions
728  // may only return a single DAP object and not a collection of objects. The name suffix
729  // "_unwrap" is used as a signal from the function to the the various response
730  // builders and transmitters that the representation needs to be altered before
731  // transmission, and that in fact is what happens in our friend
732  // promote_function_output_structures()
733  promote_function_output_structures(/*&*/fdds);
734 
735  conditional_timeout_cancel();
736 
737 
738  fdds->print_constrained(out);
739 
740  if (responseCache)
741  responseCache->unlock_and_close(cache_token);
742 
743  delete fdds;
744  }
745  else {
746  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
747 
748  if (with_mime_headers)
749  set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
750 
751  conditional_timeout_cancel();
752 
753 
754  dds.print_constrained(out);
755  }
756 
757  out << flush;
758 }
759 
774 bool BESDapResponseBuilder::store_dap2_result(ostream &out, DDS &dds, ConstraintEvaluator &eval)
775 {
776  if (get_store_result().empty()) return false;
777 
778  string serviceUrl = get_store_result();
779 
780  XMLWriter xmlWrtr;
781  D4AsyncUtil d4au;
782 
783  // FIXME Keys should be read in initialize(). Also, I think the D4AsyncUtil should
784  // be removed from libdap - it is much more about how the BES processes these kinds
785  // of operations. Change this when working on the response caching for ODSIP. But...
786  // do we really need to put the style sheet in the bes.conf file? Should it be baked
787  // into the code (because we don't want people to change it)?
788  bool found;
789  string *stylesheet_ref = 0, ss_ref_value;
790  TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
791  if (found && ss_ref_value.length() > 0) {
792  stylesheet_ref = &ss_ref_value;
793  }
794 
796  if (resultCache == NULL) {
797 
803  string msg = "The Stored Result request cannot be serviced. ";
804  msg += "Unable to acquire StoredResultCache instance. ";
805  msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
806 
807  BESDEBUG("dap", "[WARNING] " << msg << endl);
808 
809  d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
810  out << xmlWrtr.get_doc();
811  out << flush;
812 
813  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - Sent AsyncRequestRejected" << endl);
814  }
815  else if (get_async_accepted().length() != 0) {
816 
820  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - serviceUrl="<< serviceUrl << endl);
821 
823  string storedResultId = "";
824  storedResultId = resultCache->store_dap2_result(dds, get_ce(), this, &eval);
825 
826  BESDEBUG("dap",
827  "BESDapResponseBuilder::store_dap2_result() - storedResultId='"<< storedResultId << "'" << endl);
828 
829  string targetURL = BESUtil::assemblePath(serviceUrl, storedResultId);
830  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - targetURL='"<< targetURL << "'" << endl);
831 
832  XMLWriter xmlWrtr;
833  d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
834  out << xmlWrtr.get_doc();
835  out << flush;
836 
837  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - sent DAP4 AsyncAccepted response" << endl);
838  }
839  else {
844  d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
845  out << xmlWrtr.get_doc();
846  out << flush;
847 
848  BESDEBUG("dap", "BESDapResponseBuilder::store_dap2_result() - sent DAP4 AsyncRequired response" << endl);
849  }
850 
851  return true;
852 
853 }
854 
858 void BESDapResponseBuilder::serialize_dap2_data_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool ce_eval)
859 {
860  BESStopWatch sw;
861  if (BESISDEBUG(TIMING_LOG)) sw.start("BESDapResponseBuilder::serialize_dap2_data_dds", "");
862 
863  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap2_data_dds() - BEGIN" << endl);
864 
865  dds.print_constrained(out);
866  out << "Data:\n";
867  out << flush;
868 
869  XDRStreamMarshaller m(out);
870 
871  // This only has an effect when the timeout in BESInterface::execute_request()
872  // is set. Otherwise it does nothing.
873  conditional_timeout_cancel();
874 
875 
876  // Send all variables in the current projection (send_p())
877  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
878  if ((*i)->send_p()) {
879  (*i)->serialize(eval, dds, m, ce_eval);
880 #ifdef CLEAR_LOCAL_DATA
881  (*i)->clear_local_data();
882 #endif
883  }
884  }
885 
886  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap2_data_dds() - END" << endl);
887 }
888 
897 void BESDapResponseBuilder::serialize_dap2_data_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval,
898  const string &boundary, const string &start, bool ce_eval)
899 {
900  BESDEBUG("dap", __PRETTY_FUNCTION__ << " BEGIN" << endl);
901 
902  // Write the MPM headers for the DDX (text/xml) part of the response
903  libdap::set_mime_ddx_boundary(out, boundary, start, dods_ddx, x_plain);
904 
905  // Make cid
906  uuid_t uu;
907  uuid_generate(uu);
908  char uuid[37];
909  uuid_unparse(uu, &uuid[0]);
910  char domain[256];
911  if (getdomainname(domain, 255) != 0 || strlen(domain) == 0) strncpy(domain, "opendap.org", 255);
912 
913  string cid = string(&uuid[0]) + "@" + string(&domain[0]);
914 
915  // Send constrained DDX with a data blob reference.
916  // FIXME Comment CID passed but ignored jhrg 10/20/15
917  dds.print_xml_writer(out, true, cid);
918 
919  // write the data part mime headers here
920  set_mime_data_boundary(out, boundary, cid, dods_data_ddx /* old value dap4_data*/, x_plain);
921 
922  XDRStreamMarshaller m(out);
923 
924  conditional_timeout_cancel();
925 
926 
927  // Send all variables in the current projection (send_p()).
928  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
929  if ((*i)->send_p()) {
930  (*i)->serialize(eval, dds, m, ce_eval);
931 #ifdef CLEAR_LOCAL_DATA
932  (*i)->clear_local_data();
933 #endif
934  }
935  }
936 
937  BESDEBUG("dap", __PRETTY_FUNCTION__ << " END" << endl);
938 }
939 
956 void BESDapResponseBuilder::send_dap2_data(ostream &data_stream, DDS &dds, ConstraintEvaluator &eval,
957  bool with_mime_headers)
958 {
959  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - BEGIN"<< endl);
960 
961 #if USE_LOCAL_TIMEOUT_SCHEME
962  // Set up the alarm.
963  establish_timeout(data_stream);
964  dds.set_timeout(d_timeout);
965 #endif
966 
967  // Split constraint into two halves
968  split_ce(eval);
969 
970  // If there are functions, parse them and eval.
971  // Use that DDS and parse the non-function ce
972  // Serialize using the second ce and the second dds
973  if (!get_btp_func_ce().empty()) {
974  BESDEBUG("dap",
975  "BESDapResponseBuilder::send_dap2_data() - Found function(s) in CE: " << get_btp_func_ce() << endl);
976 
977  string cache_token = "";
978  DDS *fdds = 0;
979  // Define a local ce evaluator so that the clause from the function parse
980  // won't get treated like selection clauses later on when serialize is called
981  // on the DDS (fdds)
982  ConstraintEvaluator func_eval;
983  BESDapResponseCache *responseCache = 0;
984 
985 #if FUNCTION_CACHING
986  responseCache = BESDapResponseCache::get_instance();
987 #endif
988 
989  if (responseCache) {
990  BESDEBUG("dap",
991  "BESDapResponseBuilder::send_dap2_data() - Using the cache for the server function CE" << endl);
992  fdds = responseCache->cache_dataset(dds, get_btp_func_ce(), this, &func_eval, cache_token);
993  }
994  else {
995  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - Cache not found; (re)calculating" << endl);
996  func_eval.parse_constraint(get_btp_func_ce(), dds);
997  fdds = func_eval.eval_function_clauses(dds);
998  }
999 
1000  // Server functions might mark variables to use their read()
1001  // methods. Clear that so the CE in d_dap2ce will control what is
1002  // sent. If that is empty (there was only a function call) all
1003  // of the variables in the intermediate DDS (i.e., the function
1004  // result) will be sent.
1005  fdds->mark_all(false);
1006 
1007  // This next step utilizes a well known static method (so really it's a function;),
1008  // promote_function_output_structures() to look for
1009  // one or more top level Structures whose name indicates (by way of ending with
1010  // "_uwrap") that their contents should be promoted (aka moved) to the top level.
1011  // This is in support of a hack around the current API where server side functions
1012  // may only return a single DAP object and not a collection of objects. The name suffix
1013  // "_unwrap" is used as a signal from the function to the the various response
1014  // builders and transmitters that the representation needs to be altered before
1015  // transmission, and that in fact is what happens in our friend
1016  // promote_function_output_structures()
1017  promote_function_output_structures(fdds);
1018 
1019  eval.parse_constraint(get_ce(), *fdds);
1020 
1021  fdds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1022 
1023  if (fdds->get_response_limit() != 0 && fdds->get_request_size(true) > fdds->get_response_limit()) {
1024  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
1025  + "KB is too large; requests for this user are limited to "
1026  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
1027  throw Error(msg);
1028  }
1029 
1030  if (with_mime_headers)
1031  set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
1032 
1033 #if FUNCTION_CACHING
1034  // This means: if we are not supposed to store the result, then serialize it.
1035  if (!store_dap2_result(data_stream, dds, eval)) {
1036  serialize_dap2_data_dds(data_stream, *fdds, eval, true /* was 'false'. jhrg 3/10/15 */);
1037  }
1038 
1039  if (responseCache)
1040  responseCache->unlock_and_close(cache_token);
1041 #else
1042  serialize_dap2_data_dds(data_stream, *fdds, eval, true /* was 'false'. jhrg 3/10/15 */);
1043 #endif
1044 
1045  delete fdds;
1046  }
1047  else {
1048  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - Simple constraint" << endl);
1049 
1050  eval.parse_constraint(get_ce(), dds); // Throws Error if the ce doesn't parse.
1051 
1052  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1053 
1054  if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
1055  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
1056  + "KB is too large; requests for this user are limited to "
1057  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
1058  throw Error(msg);
1059  }
1060 
1061  if (with_mime_headers)
1062  set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
1063 
1064  // This means: if we are not supposed to store the result, then serialize it.
1065  if (!store_dap2_result(data_stream, dds, eval)) {
1066  serialize_dap2_data_dds(data_stream, dds, eval);
1067  }
1068  }
1069 
1070  data_stream << flush;
1071 
1072  BESDEBUG("dap", "BESDapResponseBuilder::send_dap2_data() - END"<< endl);
1073 
1074 }
1075 
1076 
1090 void BESDapResponseBuilder::send_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers)
1091 {
1092  if (d_dap2ce.empty()) {
1093  if (with_mime_headers)
1094  set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
1095 
1096  dds.print_xml_writer(out, false /*constrained */, "");
1097  //dds.print(out);
1098  out << flush;
1099  return;
1100  }
1101 
1102 #if USE_LOCAL_TIMEOUT_SCHEME
1103  // Set up the alarm.
1104  establish_timeout(out);
1105  dds.set_timeout(d_timeout);
1106 #endif
1107 
1108  // Split constraint into two halves
1109  split_ce(eval);
1110 
1111  // If there are functions, parse them and eval.
1112  // Use that DDS and parse the non-function ce
1113  // Serialize using the second ce and the second dds
1114  if (!d_btp_func_ce.empty()) {
1115  string cache_token = "";
1116  DDS *fdds = 0;
1117  ConstraintEvaluator func_eval;
1118  BESDapResponseCache *responseCache = 0;
1119 
1120 #if FUNCTION_CACHING
1121  responseCache = BESDapResponseCache::get_instance();
1122 #endif
1123 
1124  if (responseCache) {
1125  fdds = responseCache->cache_dataset(dds, d_btp_func_ce, this, &func_eval, cache_token);
1126  }
1127  else {
1128  func_eval.parse_constraint(d_btp_func_ce, dds);
1129  fdds = func_eval.eval_function_clauses(dds);
1130  }
1131 
1132  // Server functions might mark variables to use their read()
1133  // methods. Clear that so the CE in d_dap2ce will control what is
1134  // sent. If that is empty (there was only a function call) all
1135  // of the variables in the intermediate DDS (i.e., the function
1136  // result) will be sent.
1137  fdds->mark_all(false);
1138 
1139  // This next step utilizes a well known static method (so really it's a function;),
1140  // promote_function_output_structures() to look for
1141  // one or more top level Structures whose name indicates (by way of ending with
1142  // "_uwrap") that their contents should be promoted (aka moved) to the top level.
1143  // This is in support of a hack around the current API where server side functions
1144  // may only return a single DAP object and not a collection of objects. The name suffix
1145  // "_unwrap" is used as a signal from the function to the the various response
1146  // builders and transmitters that the representation needs to be altered before
1147  // transmission, and that in fact is what happens in our friend
1148  // promote_function_output_structures()
1149  promote_function_output_structures(fdds);
1150 
1151  eval.parse_constraint(d_dap2ce, *fdds);
1152 
1153  if (with_mime_headers)
1154  set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
1155 
1156  conditional_timeout_cancel();
1157 
1158 
1159  fdds->print_xml_writer(out, true, "");
1160 
1161 #if FUNCTION_CACHING
1162  if (responseCache)
1163  responseCache->unlock_and_close(cache_token);
1164 #endif
1165 
1166  delete fdds;
1167  }
1168  else {
1169  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
1170 
1171  if (with_mime_headers)
1172  set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
1173 
1174  conditional_timeout_cancel();
1175 
1176 
1177  // dds.print_constrained(out);
1178  dds.print_xml_writer(out, true, "");
1179  }
1180 
1181  out << flush;
1182 }
1183 
1184 void BESDapResponseBuilder::send_dmr(ostream &out, DMR &dmr, bool with_mime_headers)
1185 {
1186  // If the CE is not empty, parse it. The projections, etc., are set as a side effect.
1187  // If the parser returns false, the expression did not parse. The parser may also
1188  // throw Error
1189  if (!d_dap4ce.empty()) {
1190 
1191  BESDEBUG("dap", "BESDapResponseBuilder::send_dmr() - Parsing DAP4 constraint: '"<< d_dap4ce << "'"<< endl);
1192 
1193  D4ConstraintEvaluator parser(&dmr);
1194  bool parse_ok = parser.parse(d_dap4ce);
1195  if (!parse_ok) throw Error(malformed_expr, "Constraint Expression (" + d_dap4ce + ") failed to parse.");
1196  }
1197  // with an empty CE, send everything. Even though print_dap4() and serialize()
1198  // don't need this, other code may depend on send_p being set. This may change
1199  // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
1200  else {
1201  dmr.root()->set_send_p(true);
1202  }
1203 
1204  if (with_mime_headers) set_mime_text(out, dap4_dmr, x_plain, last_modified_time(d_dataset), dmr.dap_version());
1205 
1206  conditional_timeout_cancel();
1207 
1208 
1209  XMLWriter xml;
1210  dmr.print_dap4(xml, /*constrained &&*/!d_dap4ce.empty() /* true == constrained */);
1211  out << xml.get_doc() << flush;
1212 }
1213 
1214 void BESDapResponseBuilder::send_dap4_data_using_ce(ostream &out, DMR &dmr, bool with_mime_headers)
1215 {
1216  if (!d_dap4ce.empty()) {
1217  D4ConstraintEvaluator parser(&dmr);
1218  bool parse_ok = parser.parse(d_dap4ce);
1219  if (!parse_ok) throw Error(malformed_expr, "Constraint Expression (" + d_dap4ce + ") failed to parse.");
1220  }
1221  // with an empty CE, send everything. Even though print_dap4() and serialize()
1222  // don't need this, other code may depend on send_p being set. This may change
1223  // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
1224  else {
1225  dmr.root()->set_send_p(true);
1226  }
1227 
1228  if (dmr.response_limit() != 0 && dmr.request_size(true) > dmr.response_limit()) {
1229  string msg = "The Request for " + long_to_string(dmr.request_size(true) / 1024)
1230  + "MB is too large; requests for this user are limited to " + long_to_string(dmr.response_limit() / 1024)
1231  + "MB.";
1232  throw Error(msg);
1233  }
1234 
1235  if (!store_dap4_result(out, dmr)) {
1236  serialize_dap4_data(out, dmr, with_mime_headers);
1237  }
1238 }
1239 
1240 void BESDapResponseBuilder::send_dap4_data(ostream &out, DMR &dmr, bool with_mime_headers)
1241 {
1242  // If a function was passed in with this request, evaluate it and use that DMR
1243  // for the remainder of this request.
1244  // TODO Add caching for these function invocations
1245  if (!d_dap4function.empty()) {
1246  D4BaseTypeFactory d4_factory;
1247  DMR function_result(&d4_factory, "function_results");
1248 
1249  // Function modules load their functions onto this list. The list is
1250  // part of libdap, not the BES.
1251  if (!ServerFunctionsList::TheList())
1252  throw Error(
1253  "The function expression could not be evaluated because there are no server functions defined on this server");
1254 
1255  D4FunctionEvaluator parser(&dmr, ServerFunctionsList::TheList());
1256  bool parse_ok = parser.parse(d_dap4function);
1257  if (!parse_ok) throw Error("Function Expression (" + d_dap4function + ") failed to parse.");
1258 
1259  parser.eval(&function_result);
1260 
1261  // Now use the results of running the functions for the remainder of the
1262  // send_data operation.
1263  send_dap4_data_using_ce(out, function_result, with_mime_headers);
1264  }
1265  else {
1266  send_dap4_data_using_ce(out, dmr, with_mime_headers);
1267  }
1268 }
1269 
1273 void BESDapResponseBuilder::serialize_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers)
1274 {
1275  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap4_data() - BEGIN" << endl);
1276 
1277  if (with_mime_headers) set_mime_binary(out, dap4_data, x_plain, last_modified_time(d_dataset), dmr.dap_version());
1278 
1279  // Write the DMR
1280  XMLWriter xml;
1281  dmr.print_dap4(xml, !d_dap4ce.empty());
1282 
1283  // now make the chunked output stream; set the size to be at least chunk_size
1284  // but make sure that the whole of the xml plus the CRLF can fit in the first
1285  // chunk. (+2 for the CRLF bytes).
1286  chunked_ostream cos(out, max((unsigned int) CHUNK_SIZE, xml.get_doc_size() + 2));
1287 
1288  conditional_timeout_cancel();
1289 
1290  // using flush means that the DMR and CRLF are in the first chunk.
1291  cos << xml.get_doc() << CRLF << flush;
1292 
1293  // Write the data, chunked with checksums
1294  D4StreamMarshaller m(cos);
1295  dmr.root()->serialize(m, dmr, !d_dap4ce.empty());
1296 #ifdef CLEAR_LOCAL_DATA
1297  dmr.root()->clear_local_data();
1298 #endif
1299  cos << flush;
1300 
1301  BESDEBUG("dap", "BESDapResponseBuilder::serialize_dap4_data() - END" << endl);
1302 }
1303 
1318 bool BESDapResponseBuilder::store_dap4_result(ostream &out, libdap::DMR &dmr)
1319 {
1320  if (get_store_result().length() != 0) {
1321  string serviceUrl = get_store_result();
1322 
1323  D4AsyncUtil d4au;
1324  XMLWriter xmlWrtr;
1325 
1326  // FIXME See above comment for store dap2 result
1327  bool found;
1328  string *stylesheet_ref = 0, ss_ref_value;
1329  TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
1330  if (found && ss_ref_value.length() > 0) {
1331  stylesheet_ref = &ss_ref_value;
1332  }
1333 
1335  if (resultCache == NULL) {
1336 
1342  string msg = "The Stored Result request cannot be serviced. ";
1343  msg += "Unable to acquire StoredResultCache instance. ";
1344  msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
1345 
1346  BESDEBUG("dap", "[WARNING] " << msg << endl);
1347  d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
1348  out << xmlWrtr.get_doc();
1349  out << flush;
1350  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - Sent AsyncRequestRejected" << endl);
1351 
1352  return true;
1353  }
1354 
1355  if (get_async_accepted().length() != 0) {
1356 
1360  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - serviceUrl="<< serviceUrl << endl);
1361 
1362  string storedResultId = "";
1363  storedResultId = resultCache->store_dap4_result(dmr, get_ce(), this);
1364 
1365  BESDEBUG("dap",
1366  "BESDapResponseBuilder::store_dap4_result() - storedResultId='"<< storedResultId << "'" << endl);
1367 
1368  string targetURL = BESUtil::assemblePath(serviceUrl, storedResultId);
1369  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - targetURL='"<< targetURL << "'" << endl);
1370 
1371  d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
1372  out << xmlWrtr.get_doc();
1373  out << flush;
1374  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - sent AsyncAccepted" << endl);
1375 
1376  }
1377  else {
1382  d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
1383  out << xmlWrtr.get_doc();
1384  out << flush;
1385  BESDEBUG("dap", "BESDapResponseBuilder::store_dap4_result() - sent AsyncAccepted" << endl);
1386  }
1387 
1388  return true;
1389  }
1390 
1391  return false;
1392 }
1393 
virtual std::string get_ce() const
Get the constraint expression.
virtual std::string get_dap4ce() const
Get the DAP4 constraint expression.
virtual void serialize_dap2_data_dds(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool ce_eval=true)
static BESDapResponseCache * get_instance()
STL namespace.
virtual void send_dap2_data(std::ostream &data_stream, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
Transmit data.
virtual string store_dap4_result(libdap::DMR &dmr, const string &constraint, BESDapResponseBuilder *rb)
virtual bool start(string name)
Definition: BESStopWatch.cc:57
virtual std::string get_dap4function() const
Get the DAP4 server side function expression.
static string assemblePath(const string &firstPart, const string &secondPart, bool addLeadingSlash=false)
Assemble path fragments making sure that they are separated by a single &#39;/&#39; character.
Definition: BESUtil.cc:747
static BESStoredDapResultCache * get_instance()
virtual void split_ce(libdap::ConstraintEvaluator &eval, const std::string &expr="")
virtual void remove_timeout() const
void set_timeout(int timeout=0)
virtual void set_dataset_name(const std::string _dataset)
Set the dataset pathname.
virtual void set_dap4function(std::string _func)
virtual void send_ddx(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:481
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)
virtual void set_ce(std::string _ce)
bool store_dap2_result(ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval)
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:43
virtual void establish_timeout(std::ostream &stream) const
virtual bool store_dap4_result(ostream &out, libdap::DMR &dmr)
virtual std::string get_dataset_name() const
Get the dataset name.
virtual void set_dap4ce(std::string _ce)
virtual void unlock_and_close(const string &target)
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)
virtual void send_dds(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool constrained=false, bool with_mime_headers=true)
Transmit a DDS.