OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
W10nJsonTransmitter.cc
Go to the documentation of this file.
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 <DataDDS.h>
45 #include <BaseType.h>
46 #include <escaping.h>
47 #include <ConstraintEvaluator.h>
48 
49 #include <BESInternalError.h>
50 #include <TheBESKeys.h>
51 #include <BESContextManager.h>
52 #include <BESDataDDSResponse.h>
53 #include <BESDDSResponse.h>
54 #include <BESDapError.h>
55 #include <BESDapNames.h>
56 #include <BESDataNames.h>
57 #include <BESDebug.h>
58 #include <BESSyntaxUserError.h>
59 
60 #include "W10nJsonTransmitter.h"
61 
62 #include "W10nJsonTransform.h"
63 #include "W10NNames.h"
64 #include "w10n_utils.h"
65 
66 
67 using namespace ::libdap;
68 
69 #define W10N_JSON_TEMP_DIR "/tmp"
70 
71 string W10nJsonTransmitter::temp_dir;
72 
85 {
88 
89  if (W10nJsonTransmitter::temp_dir.empty()) {
90  // Where is the temp directory for creating these files
91  bool found = false;
92  string key = "W10nJson.Tempdir";
93  TheBESKeys::TheKeys()->get_value(key, W10nJsonTransmitter::temp_dir, found);
94  if (!found || W10nJsonTransmitter::temp_dir.empty()) {
95  W10nJsonTransmitter::temp_dir = W10N_JSON_TEMP_DIR;
96  }
97  string::size_type len = W10nJsonTransmitter::temp_dir.length();
98  if (W10nJsonTransmitter::temp_dir[len - 1] == '/') {
99  W10nJsonTransmitter::temp_dir = W10nJsonTransmitter::temp_dir.substr(0, len - 1);
100  }
101  }
102 }
103 
104 
105 
110 void W10nJsonTransmitter::checkConstraintForW10nCompatibility(const string &ce){
111  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::checkConstraintForW10nCompatibility() - BEGIN. ce: "<< ce << endl);
112 
113  string projectionClause = getProjectionClause(ce);
114  int firstComma = projectionClause.find(",");
115 
116  if(firstComma != -1){
117  string msg = "The w10n protocol only allows one variable to be selected at a time. ";
118  msg += "The constraint expression '" + ce + "' requests more than one.";
119  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::checkConstraintForW10nCompatibility() - ERROR! "<< msg << endl);
120  throw BESSyntaxUserError(msg , __FILE__, __LINE__);
121  }
122  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::checkConstraintForW10nCompatibility() - END: " << endl);
123 
124 }
125 
129 string W10nJsonTransmitter::getProjectionClause(const string &constraintExpression){
130 
131  string projectionClause = constraintExpression;
132  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::getProjectionClause() - constraintExpression: "<< constraintExpression << endl);
133 
134  int firstAmpersand = constraintExpression.find("&");
135  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::getProjectionClause() - firstAmpersand: "<< firstAmpersand << endl);
136  if(firstAmpersand>=0)
137  projectionClause = constraintExpression.substr(0,firstAmpersand);
138 
139  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::getProjectionClause() - CE projection clause: "<< projectionClause << endl);
140 
141  return projectionClause;
142 }
143 
147 string W10nJsonTransmitter::getProjectedVariableName(const string &constraintExpression){
148 
149  string varName = getProjectionClause(constraintExpression);
150 
151  int firstSquareBracket = varName.find("[");
152  if(firstSquareBracket != -1){
153  varName = varName.substr(0,firstSquareBracket);
154  }
155  return varName;
156 
157 
158 }
159 
160 
161 
162 
163 
180 {
181  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - BEGIN." << endl);
182 
183  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
184  if (!bdds) {
185  string msg = "Dynamic cast of BESResponseObject to BESDataDDSResponse FAILED.";
186  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
187  throw BESInternalError(msg, __FILE__, __LINE__);
188  }
189 
190  DataDDS *dds = bdds->get_dds();
191  if (!dds){
192  string msg = "No DataDDS has been created for transmit";
193  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
194  throw BESInternalError(msg, __FILE__, __LINE__);
195  }
196 
197  ostream &o_strm = dhi.get_output_stream();
198  if (!o_strm){
199  string msg = "Output stream is not set, cannot return data as JSON";
200  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
201  throw BESInternalError(msg, __FILE__, __LINE__);
202  }
203 
204 
205 
206  ConstraintEvaluator &eval = bdds->get_ce();
207 
208 
209  // ticket 1248 jhrg 2/23/09
210  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
211 
212  checkConstraintForW10nCompatibility(ce);
213 
214  try {
215  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - Parsing the constraint: "<< ce << endl);
216  eval.parse_constraint(ce, *dds);
217  }
218  catch (Error &e) {
219  string msg = "Failed to parse the constraint expression. Msg: "+ e.get_error_message();
220  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
221  throw BESDapError(msg, 0, e.get_error_code(),__FILE__, __LINE__);
222  }
223  catch (...) {
224  string msg = "Failed to parse the constraint expression. Unknown exception caught";
225  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
226  throw BESInternalError(msg, __FILE__, __LINE__);
227  }
228 
230 
231 
232  // now we need to read the data
233  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - reading data into DataDDS" << endl);
234 
235  try {
236  // Handle *functional* constraint expressions specially
237  if (eval.function_clauses()) {
238  BESDEBUG(W10N_DEBUG_KEY, "processing a functional constraint clause(s)." << endl);
239  DataDDS *tmp_dds = eval.eval_function_clauses(*dds);
240  bdds->set_dds(tmp_dds);
241  delete dds;
242  dds = tmp_dds;
243  }
244  else {
245  // Iterate through the variables in the DataDDS and read
246  // in the data if the variable has the send flag set.
247  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
248  if ((*i)->send_p()) {
249  (*i)->intern_data(eval, *dds);
250  }
251  }
252  }
253  }
254  catch (Error &e) {
255  string msg = "Failed to read data! Msg: " + e.get_error_message();
256  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
257  throw BESDapError(msg, 0, e.get_error_code(),__FILE__, __LINE__);
258  }
259  catch (...) {
260  string msg = "Failed to read data: Unknown exception caught";
261  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
262  throw BESInternalError(msg, __FILE__, __LINE__);
263  }
264 
265  try {
266 
267 
268  W10nJsonTransform ft(dds, dhi, &o_strm);
269 
270  string varName = getProjectedVariableName(ce);
271 
272  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - Sending w10n meta response for variable "
273  << varName << endl);
274  ft.sendW10nDataForVariable(varName);
275  }
276  catch (BESError &e) {
277  string msg = "Failed to transmit data as w10n JSON. Msg: " + e.get_message();
278  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
279  throw;
280  }
281  catch (...) {
282  string msg = "Failed to transmit data as w10n JSON. Unknown Error.";
283  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - ERROR! "<< msg << endl);
284  throw BESInternalError(msg, __FILE__, __LINE__);
285  }
286 
287  cleanupW10nContexts();
288 
289  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_data() - END. Done transmitting JSON" << endl);
290 }
291 
292 
293 
310 {
311  BESDDSResponse *bdds = dynamic_cast<BESDDSResponse *>(obj);
312  if (!bdds)
313  throw BESInternalError("cast error", __FILE__, __LINE__);
314 
315  DDS *dds = bdds->get_dds();
316  if (!dds)
317  throw BESInternalError("No DDS has been created for transmit", __FILE__, __LINE__);
318 
319  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - parsing the constraint" << endl);
320 
321  ConstraintEvaluator &eval = bdds->get_ce();
322 
323  ostream &o_strm = dhi.get_output_stream();
324  if (!o_strm)
325  throw BESInternalError("Output stream is not set, can not return as JSON", __FILE__, __LINE__);
326 
327  // ticket 1248 jhrg 2/23/09
328  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
329 
330  checkConstraintForW10nCompatibility(ce);
331 
332  try {
333  eval.parse_constraint(ce, *dds);
334  }
335  catch (Error &e) {
336  throw BESInternalError("Failed to parse the constraint expression: " + e.get_error_message(), __FILE__, __LINE__);
337  }
338  catch (...) {
339  throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__, __LINE__);
340  }
341 
342 
343  W10nJsonTransform ft(dds, dhi, &o_strm);
344 
345  string varName = getProjectedVariableName(ce);
346 
347  if(varName.length() == 0){
348  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - Sending w10n meta response for DDS" << endl);
349  ft.sendW10nMetaForDDS();
350  }
351  else {
352  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - Sending w10n meta response for variable "
353  << varName << endl);
354  ft.sendW10nMetaForVariable(varName, true);
355  }
356 
357  cleanupW10nContexts();
358 
359  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::send_metadata() - done transmitting JSON" << endl);
360 }
361 
371 void W10nJsonTransmitter::return_temp_stream(const string &filename, ostream &strm)
372 {
373  // int bytes = 0 ; // Not used; jhrg 3/16/11
374  ifstream os;
375  os.open(filename.c_str(), ios::binary | ios::in);
376  if (!os) {
377  string err = "Can not connect to file " + filename;
378  BESInternalError pe(err, __FILE__, __LINE__);
379  throw pe;
380  }
381  int nbytes;
382  char block[4096];
383 
384  os.read(block, sizeof block);
385  nbytes = os.gcount();
386  if (nbytes > 0) {
387  strm.write(block, nbytes);
388  //bytes += nbytes ;
389  }
390  else {
391  // close the stream before we leave.
392  os.close();
393 
394  string err = (string) "0XAAE234F: failed to stream. Internal server "
395  + "error, got zero count on stream buffer." + filename;
396  BESInternalError pe(err, __FILE__, __LINE__);
397  throw pe;
398  }
399  while (os) {
400  os.read(block, sizeof block);
401  nbytes = os.gcount();
402  strm.write(block, nbytes);
403  //write( fileno( stdout ),(void*)block, nbytes ) ;
404  //bytes += nbytes ;
405  }
406  os.close();
407 }
408 
409 
410 
411 void W10nJsonTransmitter::cleanupW10nContexts()
412 {
413  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::cleanupW10nContexts() - Removing context" << W10N_META_OBJECT_KEY << endl);
415 
416  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::cleanupW10nContexts() - Removing context" << W10N_CALLBACK_KEY << endl);
418 
419  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::cleanupW10nContexts() - Removing context" << W10N_FLATTEN_KEY << endl);
421 
422  BESDEBUG(W10N_DEBUG_KEY, "W10nJsonTransmitter::cleanupW10nContexts() - Removing context" << W10N_TRAVERSE_KEY << endl);
424 
425 }
static void send_metadata(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a JSON file.
#define DATA_SERVICE
Definition: BESDapNames.h:71
static void send_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a JSON file.
void set_dds(DataDDS *ddsIn)
Set the response object's DDS.
exception thrown if inernal error encountered
ConstraintEvaluator & get_ce()
#define W10N_TRAVERSE_KEY
Definition: W10NNames.h:9
W10nJsonTransmitter()
Construct the W10nJsonTransmitter.
Represents an OPeNDAP DDS DAP2 data object within the BES.
#define W10N_FLATTEN_KEY
Definition: W10NNames.h:7
void checkConstrainedDDSForW10nDataCompatibility(libdap::DDS *dds)
Definition: w10n_utils.cc:334
#define W10N_META_OBJECT_KEY
Definition: W10NNames.h:6
virtual bool add_method(string method_name, p_transmitter trans_method)
error thrown if there is a user syntax error in the request or any other user error ...
static class NCMLUtil overview
Used to transform a DDS into a w10n JSON metadata or w10n JSON data document.
virtual string get_message()
get the error message for this exception
Definition: BESError.h:94
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
#define W10N_CALLBACK_KEY
Definition: W10NNames.h:8
#define DDX_SERVICE
Definition: BESDapNames.h:66
error object created from libdap error objects and can handle those errors
Definition: BESDapError.h:51
virtual void unset_context(const string &name)
set context in the BES
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
#define W10N_DEBUG_KEY
Definition: W10NNames.h:4
#define W10N_JSON_TEMP_DIR
static BESContextManager * TheManager()
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:453
#define POST_CONSTRAINT
Definition: BESDataNames.h:43
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.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:48
Abstract base class representing a specific set of information in response to a request to the BES...