bes  Updated for version 3.20.13
W10nJsonTransmitter.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 //
3 // W10nJsonTransmitter.cc
4 //
5 // This file is part of BES JSON File Out Module
6 //
7 // Copyright (c) 2014 OPeNDAP, Inc.
8 // Author: Nathan Potter <ndp@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 // (c) COPYRIGHT URI/MIT 1995-1999
26 // Please read the full copyright statement in the file COPYRIGHT_URI.
27 //
28 
29 #include "config.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 
38 #include <sys/types.h> // For umask
39 #include <sys/stat.h>
40 
41 #include <iostream>
42 #include <fstream>
43 
44 #include <libdap/DataDDS.h>
45 #include <libdap/BaseType.h>
46 #include <libdap/escaping.h>
47 #include <libdap/ConstraintEvaluator.h>
48 
49 #include <BESUtil.h>
50 #include <BESInternalError.h>
51 #include <BESDapError.h>
52 #include <BESDapError.h>
53 #include <TheBESKeys.h>
54 #include <BESContextManager.h>
55 #include <BESDataDDSResponse.h>
56 #include <BESDDSResponse.h>
57 #include <BESDapError.h>
58 #include <BESDapNames.h>
59 #include <BESDataNames.h>
60 #include <BESDebug.h>
61 #include <BESStopWatch.h>
62 #include <BESSyntaxUserError.h>
63 #include <BESDapResponseBuilder.h>
64 #include <RequestServiceTimer.h>
65 
66 #include "W10nJsonTransmitter.h"
67 
68 #include "W10nJsonTransform.h"
69 #include "W10NNames.h"
70 #include "w10n_utils.h"
71 
72 using namespace ::libdap;
73 
74 #define MODULE "w10n"
75 #define prolog string("W10nJsonTransmitter::").append(__func__).append("() - ")
76 
77 #define W10N_JSON_TEMP_DIR "/tmp"
78 
79 string W10nJsonTransmitter::temp_dir;
80 
94 {
95  add_method(DATA_SERVICE, W10nJsonTransmitter::send_data);
96  add_method(DDX_SERVICE, W10nJsonTransmitter::send_metadata);
97 
98  if (W10nJsonTransmitter::temp_dir.empty()) {
99  // Where is the temp directory for creating these files
100  bool found = false;
101  string key = "W10nJson.Tempdir";
102  TheBESKeys::TheKeys()->get_value(key, W10nJsonTransmitter::temp_dir, found);
103  if (!found || W10nJsonTransmitter::temp_dir.empty()) {
104  W10nJsonTransmitter::temp_dir = W10N_JSON_TEMP_DIR;
105  }
106  string::size_type len = W10nJsonTransmitter::temp_dir.length();
107  if (W10nJsonTransmitter::temp_dir[len - 1] == '/') {
108  W10nJsonTransmitter::temp_dir = W10nJsonTransmitter::temp_dir.substr(0, len - 1);
109  }
110  }
111 }
112 
117 void W10nJsonTransmitter::checkConstraintForW10nCompatibility(const string &ce)
118 {
119  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::checkConstraintForW10nCompatibility() - BEGIN. ce: "<< ce << endl);
120 
121  string projectionClause = getProjectionClause(ce);
122  int firstComma = projectionClause.find(",");
123 
124  if (firstComma != -1) {
125  string msg = "The w10n protocol only allows one variable to be selected at a time. ";
126  msg += "The constraint expression '" + ce + "' requests more than one.";
127  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::checkConstraintForW10nCompatibility() - ERROR! "<< msg << endl);
128  throw BESSyntaxUserError(msg, __FILE__, __LINE__);
129  }
130 
131  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::checkConstraintForW10nCompatibility() - END: " << endl);
132 }
133 
137 string W10nJsonTransmitter::getProjectionClause(const string &constraintExpression)
138 {
139  string projectionClause = constraintExpression;
140  BESDEBUG(W10N_DEBUG_KEY,
141  "W10nJsonTransmitter::getProjectionClause() - constraintExpression: "<< constraintExpression << endl);
142 
143  int firstAmpersand = constraintExpression.find("&");
144  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::getProjectionClause() - firstAmpersand: "<< firstAmpersand << endl);
145  if (firstAmpersand >= 0) projectionClause = constraintExpression.substr(0, firstAmpersand);
146 
147  BESDEBUG(W10N_DEBUG_KEY,
148  "W10nJsonTransmitter::getProjectionClause() - CE projection clause: "<< projectionClause << endl);
149 
150  return projectionClause;
151 }
152 
156 string W10nJsonTransmitter::getProjectedVariableName(const string &constraintExpression)
157 {
158  string varName = getProjectionClause(constraintExpression);
159 
160  int firstSquareBracket = varName.find("[");
161  if (firstSquareBracket != -1) {
162  varName = varName.substr(0, firstSquareBracket);
163  }
164 
165  return varName;
166 }
167 
168 struct ContextCleanup {
169  ContextCleanup() {}
170  ~ContextCleanup() {
171  BESDEBUG(W10N_DEBUG_KEY, "Cleanup w10n contexts" << endl);
172  W10nJsonTransmitter::cleanupW10nContexts();
173  }
174 };
175 
193 {
194 #ifndef NDEBUG
195  BESStopWatch sw;
196  if (BESDebug::IsSet(TIMING_LOG_KEY)) sw.start("W10nJsonTransmitter::send_data", dhi.data[REQUEST_ID]);
197 #endif
198 
199  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - BEGIN." << endl);
200 
201  // When 'cleanup' goes out of scope, cleanup the w10n contexts - incl. exceptions.
202  ContextCleanup cleanup;
203 
204  try {
205  BESDapResponseBuilder responseBuilder;
206 
207  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - reading data into DataDDS" << endl);
208 
209  DDS *loaded_dds = responseBuilder.intern_dap2_data(obj, dhi);
210 
211  checkConstraintForW10nCompatibility(dhi.data[POST_CONSTRAINT]);
212  w10n::checkConstrainedDDSForW10nDataCompatibility(loaded_dds);
213 
214  ostream &o_strm = dhi.get_output_stream();
215  if (!o_strm) throw BESInternalError("Output stream is not set, can not return as JSON", __FILE__, __LINE__);
216 
217  W10nJsonTransform ft(loaded_dds, dhi, &o_strm);
218 
219  string varName = getProjectedVariableName(dhi.data[POST_CONSTRAINT]);
220 
221  // Verify the request hasn't exceeded bes_timeout.
222  RequestServiceTimer::TheTimer()->throw_if_timeout_expired("ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
223 
224  // Now that we are ready to start building the response data we
225  // cancel any pending timeout alarm according to the configuration.
227 
228  BESDEBUG(W10N_DEBUG_KEY,
229  "W10nJsonTransmitter::send_data() - Sending w10n data response for variable " << varName << endl);
230 
231  ft.sendW10nDataForVariable(varName);
232  }
233  catch (Error &e) {
234  throw BESDapError("Failed to read data! Msg: " + e.get_error_message(), false, e.get_error_code(),
235  __FILE__, __LINE__);
236  }
237  catch (BESError &e) {
238  throw;
239  }
240  catch (...) {
241  throw BESInternalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
242  }
243 
244  // cleanupW10nContexts(); See above where an instance
245 
246  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - END. Done transmitting JSON" << endl);
247 }
248 
266 {
267 #ifndef NDEBUG
268  BESStopWatch sw;
269  if (BESDebug::IsSet(TIMING_LOG_KEY)) sw.start("W10nJsonTransmitter::send_metadata", dhi.data[REQUEST_ID]);
270 #endif
271 
272  ContextCleanup cleanup;
273 
274  BESDDSResponse *bdds = dynamic_cast<BESDDSResponse *>(obj);
275  if (!bdds) throw BESInternalError("cast error", __FILE__, __LINE__);
276 
277  DDS *dds = bdds->get_dds();
278  if (!dds) throw BESInternalError("No DDS has been created for transmit", __FILE__, __LINE__);
279 
280  ConstraintEvaluator &eval = bdds->get_ce();
281 
282  ostream &o_strm = dhi.get_output_stream();
283  if (!o_strm) throw BESInternalError("Output stream is not set, can not return as JSON", __FILE__, __LINE__);
284 
285  // ticket 1248 jhrg 2/23/09
286  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
287 
288  checkConstraintForW10nCompatibility(ce);
289 
290  try {
291  eval.parse_constraint(ce, *dds);
292  }
293  catch (Error &e) {
294  throw BESDapError("Failed to parse the constraint expression: " + e.get_error_message(), false,
295  e.get_error_code(), __FILE__, __LINE__);
296  }
297  catch (...) {
298  throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__,
299  __LINE__);
300  }
301 
302  W10nJsonTransform ft(dds, dhi, &o_strm);
303 
304  string varName = getProjectedVariableName(ce);
305 
306  if (varName.length() == 0) {
307  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - Sending w10n meta response for DDS" << endl);
308  ft.sendW10nMetaForDDS();
309  }
310  else {
311  BESDEBUG(W10N_DEBUG_KEY,
312  "W10nJsonTransmitter::send_metadata() - Sending w10n meta response for variable " << varName << endl);
313  ft.sendW10nMetaForVariable(varName, true);
314  }
315 
316  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - done transmitting JSON" << endl);
317 }
318 
322 void W10nJsonTransmitter::cleanupW10nContexts()
323 {
324  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::cleanupW10nContexts() - Removing contexts" << endl);
325 
326  BESContextManager::TheManager()->unset_context(W10N_META_OBJECT_KEY);
327 
328  BESContextManager::TheManager()->unset_context(W10N_CALLBACK_KEY);
329 
330  BESContextManager::TheManager()->unset_context(W10N_FLATTEN_KEY);
331 
332  BESContextManager::TheManager()->unset_context(W10N_TRAVERSE_KEY);
333 }
virtual void unset_context(const std::string &name)
set context in the BES
Holds a DDS object within the BES.
libdap::ConstraintEvaluator & get_ce()
libdap::DDS * get_dds()
error object created from libdap error objects and can handle those errors
Definition: BESDapError.h:59
virtual libdap::DDS * intern_dap2_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
Structure storing information used by the BES to handle the request.
std::map< std::string, std::string > data
the map of string data that will be required for the current request.
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:168
Base exception class for the BES with basic string message.
Definition: BESError.h:59
exception thrown if internal error encountered
Abstract base class representing a specific set of information in response to a request to the BES.
virtual bool start(std::string name)
Definition: BESStopWatch.cc:67
error thrown if there is a user syntax error in the request or any other user error
static void conditional_timeout_cancel()
Checks if the timeout alarm should be canceled based on the value of the BES key BES....
Definition: BESUtil.cc:895
static RequestServiceTimer * TheTimer()
Return a pointer to a singleton timer instance. If an instance does not exist it will create and init...
void throw_if_timeout_expired(const std::string &message, const std::string &file, const int line)
Checks the RequestServiceTimer to determine if the time spent servicing the request at this point has...
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71
static void send_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a JSON file.
static void send_metadata(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a JSON file.
W10nJsonTransmitter()
Construct the W10nJsonTransmitter.