OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
FONcTransmitter.cc
Go to the documentation of this file.
1 // FONcTransmitter.cc
2 
3 // This file is part of BES Netcdf File Out Module
4 
5 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
6 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact University Corporation for Atmospheric Research at
23 // 3080 Center Green Drive, Boulder, CO 80301
24 
25 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
26 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
27 //
28 // Authors:
29 // pwest Patrick West <pwest@ucar.edu>
30 // jgarcia Jose Garcia <jgarcia@ucar.edu>
31 
32 #include "config.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 
41 #include <sys/types.h> // For umask
42 #include <sys/stat.h>
43 
44 #include <iostream>
45 #include <fstream>
46 
47 #include <DataDDS.h>
48 #include <BaseType.h>
49 #include <escaping.h>
50 #include <ConstraintEvaluator.h>
51 
52 #include <BESInternalError.h>
53 #include <TheBESKeys.h>
54 #include <BESContextManager.h>
55 #include <BESDataDDSResponse.h>
56 #include <BESDapNames.h>
57 #include <BESDataNames.h>
58 #include <BESDebug.h>
59 
60 #include "FONcTransmitter.h"
61 #include "FONcTransform.h"
62 
63 using namespace ::libdap;
64 
65 #define FONC_TEMP_DIR "/tmp"
66 
67 #define RETURNAS_NETCDF "netcdf"
68 #define RETURNAS_NETCDF4 "netcdf-4"
69 
70 string FONcTransmitter::temp_dir;
71 
72 #if 0
73 // I added this because I was hoping to make the byte --> short option added
74 // via a pull request from NSIDC as controllable via a conf file parameter.
75 // jhrg 4/1/15
87 static void read_key_value(const std::string &key_name, bool &key_value, bool &is_key_set)
88 {
89  if (is_key_set == false) {
90  bool key_found = false;
91  string doset;
92  TheBESKeys::TheKeys()->get_value(key_name, doset, key_found);
93  if (key_found) {
94  // It was set in the conf file
95  is_key_set = true;
96 
97  doset = BESUtil::lowercase(doset);
98  key_value = (doset == "true" || doset == "yes");
99  }
100  }
101 }
102 #endif
103 
117 {
119 
120  if (FONcTransmitter::temp_dir.empty()) {
121  // Where is the temp directory for creating these files
122  bool found = false;
123  string key = "FONc.Tempdir";
124  TheBESKeys::TheKeys()->get_value(key, FONcTransmitter::temp_dir, found);
125  if (!found || FONcTransmitter::temp_dir.empty()) {
126  FONcTransmitter::temp_dir = FONC_TEMP_DIR;
127  }
128  string::size_type len = FONcTransmitter::temp_dir.length();
129  if (FONcTransmitter::temp_dir[len - 1] == '/') {
130  FONcTransmitter::temp_dir = FONcTransmitter::temp_dir.substr(0, len - 1);
131  }
132  }
133 }
134 
151 {
152  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
153  if (!bdds)
154  throw BESInternalError("cast error", __FILE__, __LINE__);
155 
156  DataDDS *dds = bdds->get_dds();
157  if (!dds)
158  throw BESInternalError("No DataDDS has been created for transmit", __FILE__, __LINE__);
159 
160  BESDEBUG("fonc", "FONcTransmitter::send_data - parsing the constraint" << endl);
161 
162  ConstraintEvaluator &eval = bdds->get_ce();
163 
164  string ncVersion = dhi.data[RETURN_CMD] ;
165 
166  ostream &strm = dhi.get_output_stream();
167  if (!strm)
168  throw BESInternalError("Output stream is not set, can not return as", __FILE__, __LINE__);
169 
170  // ticket 1248 jhrg 2/23/09
171  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
172  try {
173  eval.parse_constraint(ce, *dds);
174  }
175  catch (Error &e) {
176  throw BESInternalError("Failed to parse the constraint expression: " + e.get_error_message(), __FILE__, __LINE__);
177  }
178  catch (...) {
179  throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__, __LINE__);
180  }
181 
182  // The dataset_name is no longer used in the constraint evaluator, so no
183  // need to get here. Plus, just getting the first containers dataset
184  // name would not have worked with multiple containers.
185  // pwest Jan 4, 2009
186  // string dataset_name = "";
187 
188  // now we need to read the data
189  BESDEBUG("fonc", "FONcTransmitter::send_data - reading data into DataDDS" << endl);
190 
191  // ADB: remember when we're using a temp DDS
192  // bool using_temp_dds = false; See comment below about set_dds(). jhrg 8/8/14
193 
194  try {
195  // Handle *functional* constraint expressions specially
196  if (eval.function_clauses()) {
197  BESDEBUG("fonc", "processing a functional constraint clause(s)." << endl);
198  DataDDS *tmp_dds = eval.eval_function_clauses(*dds);
199  // I think setting this fixes the issue Aron (ADB) reported. jhrg 8/8/14
200  bdds->set_dds(tmp_dds);
201  delete dds;
202  dds = tmp_dds;
203  // ADB: don't delete DDS here because it will already be
204  // deleted by ~BESDataDDSResponse().
205  // delete dds;
206 
207  // ADB: instead, remember that you're using a temp and
208  // delete it later
209  //using_temp_dds = true;
210  }
211  else {
212  // Iterate through the variables in the DataDDS and read
213  // in the data if the variable has the send flag set.
214 
215  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
216  if ((*i)->send_p()) {
217  (*i)->intern_data(eval, *dds);
218  }
219  }
220  }
221  }
222  catch (Error &e) {
223  throw BESInternalError("Failed to read data: " + e.get_error_message(), __FILE__, __LINE__);
224  }
225  catch (...) {
226  throw BESInternalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
227  }
228 
229  string temp_file_name = FONcTransmitter::temp_dir + '/' + "ncXXXXXX";
230  vector<char> temp_full(temp_file_name.length() + 1);
231  string::size_type len = temp_file_name.copy(&temp_full[0], temp_file_name.length());
232  temp_full[len] = '\0';
233  // cover the case where older versions of mkstemp() create the file using
234  // a mode of 666.
235  mode_t original_mode = umask(077);
236  int fd = mkstemp(&temp_full[0]);
237  umask(original_mode);
238 
239  if (fd == -1)
240  throw BESInternalError("Failed to open the temporary file: " + temp_file_name, __FILE__, __LINE__);
241 
242  // transform the OPeNDAP DataDDS to the netcdf file
243  BESDEBUG("fonc", "FONcTransmitter::send_data - transforming into temporary file " << &temp_full[0] << endl);
244 
245  try {
246  FONcTransform ft(dds, dhi, &temp_full[0], ncVersion);
247  ft.transform();
248 
249  BESDEBUG("fonc", "FONcTransmitter::send_data - transmitting temp file " << &temp_full[0] << endl);
250  FONcTransmitter::return_temp_stream(&temp_full[0], strm, ncVersion);
251  }
252  catch (BESError &e) {
253  close(fd);
254  (void) unlink(&temp_full[0]);
255  // ADB: clean-up temp dds
256  // if (using_temp_dds) delete dds; See comment above. jhrg 8/8/14
257  throw;
258  }
259  catch (...) {
260  close(fd);
261  (void) unlink(&temp_full[0]);
262  // ADB: clean-up temp dds
263  //if (using_temp_dds) delete dds;
264 
265  throw BESInternalError("File out netcdf, was not able to transform to netcdf, unknown error", __FILE__, __LINE__);
266  }
267 
268  close(fd);
269  (void) unlink(&temp_full[0]);
270  // ADB: clean-up temp dds
271  //if (using_temp_dds) delete dds;
272 
273  BESDEBUG("fonc", "FONcTransmitter::send_data - done transmitting to netcdf" << endl);
274 }
275 
285 void FONcTransmitter::return_temp_stream(const string &filename,
286  ostream &strm,
287  const string &ncVersion)
288 {
289  // int bytes = 0 ; // Not used; jhrg 3/16/11
290  ifstream os;
291  os.open(filename.c_str(), ios::binary | ios::in);
292  if (!os) {
293  string err = "Can not connect to file " + filename;
294  BESInternalError pe(err, __FILE__, __LINE__);
295  throw pe;
296  }
297  int nbytes;
298  char block[4096];
299 
300  os.read(block, sizeof block);
301  nbytes = os.gcount();
302  if (nbytes > 0) {
303  bool found = false;
304  string context = "transmit_protocol";
305  string protocol = BESContextManager::TheManager()->get_context(context, found);
306  if (protocol == "HTTP") {
307  strm << "HTTP/1.0 200 OK\n";
308  strm << "Content-type: application/octet-stream\n";
309  strm << "Content-Description: " << "BES dataset" << "\n";
310  if ( ncVersion == RETURNAS_NETCDF4 ) {
311  strm << "Content-Disposition: filename=" << filename << ".nc4;\n\n";
312  }
313  else {
314  strm << "Content-Disposition: filename=" << filename << ".nc;\n\n";
315  }
316  strm << flush;
317  }
318  strm.write(block, nbytes);
319  //bytes += nbytes ;
320  }
321  else {
322  // close the stream before we leave.
323  os.close();
324 
325  string err = (string) "0XAAE234F: failed to stream. Internal server "
326  + "error, got zero count on stream buffer." + filename;
327  BESInternalError pe(err, __FILE__, __LINE__);
328  throw pe;
329  }
330  while (os) {
331  os.read(block, sizeof block);
332  nbytes = os.gcount();
333  strm.write(block, nbytes);
334  //write( fileno( stdout ),(void*)block, nbytes ) ;
335  //bytes += nbytes ;
336  }
337  os.close();
338 }
339 
#define DATA_SERVICE
Definition: BESDapNames.h:71
void set_dds(DataDDS *ddsIn)
Set the response object's DDS.
exception thrown if inernal error encountered
static string lowercase(const string &s)
Convert a string to all lower case.
Definition: BESUtil.cc:182
FONcTransmitter()
Construct the FONcTransmitter, adding it with name netcdf to be able to transmit a data response...
#define RETURNAS_NETCDF4
virtual bool add_method(string method_name, p_transmitter trans_method)
static void send_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a netcdf file.
virtual string get_context(const string &name, bool &found)
retrieve the value of the specified context from the BES
#define RETURN_CMD
Definition: BESDataNames.h:53
static class NCMLUtil overview
#define FONC_TEMP_DIR
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
virtual void transform()
Transforms each of the variables of the DataDDS to the NetCDF file.
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...
Transformation object that converts an OPeNDAP DataDDS to a netcdf file.
Definition: FONcTransform.h:62