OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
JPEG2000Transmitter.cc
Go to the documentation of this file.
1 // JPEG2000Transmitter.cc
2 
3 // This file is part of BES GDAL File Out Module
4 
5 // Copyright (c) 2012 OPeNDAP, Inc.
6 // Author: James Gallagher <jgallagher@opendap.org>
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 #include "config.h"
26 
27 #include <unistd.h>
28 
29 #include <cstdio>
30 #include <cstdlib>
31 
32 #include <sys/types.h> // For umask
33 #include <sys/stat.h>
34 
35 #include <iostream>
36 #include <fstream>
37 
38 #include <DataDDS.h>
39 #include <BaseType.h>
40 #include <escaping.h>
41 
42 using namespace libdap;
43 
44 #include "JPEG2000Transmitter.h"
45 #include "FONgTransform.h"
46 
47 #include <BESInternalError.h>
48 #include <BESContextManager.h>
49 #include <BESDataDDSResponse.h>
50 #include <BESDapNames.h>
51 #include <BESDataNames.h>
52 #include <BESDebug.h>
53 
54 #include <TheBESKeys.h>
55 
56 #define JPEG2000_TEMP_DIR "/tmp"
57 #define JPEG2000_GCS "WGS84"
58 
59 string JPEG2000Transmitter::temp_dir;
61 
79 {
80  // DATA_SERVICE == "dods"
82 
83  if (JPEG2000Transmitter::temp_dir.empty()) {
84  // Where is the temp directory for creating these files
85  bool found = false;
86  string key = "JPEG2000.Tempdir";
87  TheBESKeys::TheKeys()->get_value(key, JPEG2000Transmitter::temp_dir, found);
88  if (!found || JPEG2000Transmitter::temp_dir.empty()) {
89  JPEG2000Transmitter::temp_dir = JPEG2000_TEMP_DIR;
90  }
91  string::size_type len = JPEG2000Transmitter::temp_dir.length();
92  if (JPEG2000Transmitter::temp_dir[len - 1] == '/') {
93  JPEG2000Transmitter::temp_dir = JPEG2000Transmitter::temp_dir.substr(0, len - 1);
94  }
95  }
96 
98  // Use what as the default Geographic coordinate system?
99  bool found = false;
100  string key = "JPEG2000.Default_GCS";
102  if (!found || JPEG2000Transmitter::default_gcs.empty()) {
104  }
105  }
106 }
107 
124 {
125  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
126  if (!bdds) {
127  throw BESInternalError("cast error", __FILE__, __LINE__);
128  }
129 
130  DataDDS *dds = bdds->get_dds();
131  if (!dds)
132  throw BESInternalError("No DataDDS has been created for transmit", __FILE__, __LINE__);
133 
134  ostream &strm = dhi.get_output_stream();
135  if (!strm)
136  throw BESInternalError("Output stream is not set, cannot return as", __FILE__, __LINE__);
137 
138  BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - parsing the constraint" << endl);
139 
140  // ticket 1248 jhrg 2/23/09
141  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
142  try {
143  bdds->get_ce().parse_constraint(ce, *dds);
144  }
145  catch (Error &e) {
146  throw BESInternalError("Failed to parse the constraint expression: " + e.get_error_message(), __FILE__, __LINE__);
147  }
148  catch (...) {
149  throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__, __LINE__);
150  }
151 
152  // now we need to read the data
153  BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - reading data into DataDDS" << endl);
154 
155  // Huh? Put the template for the temp file name in a char array. Use vector<char>
156  // to avoid using new/delete.
157  string temp_file_name = JPEG2000Transmitter::temp_dir + '/' + "jp2XXXXXX";
158  vector<char> temp_file(temp_file_name.length() + 1);
159  string::size_type len = temp_file_name.copy(&temp_file[0], temp_file_name.length());
160  temp_file[len] = '\0';
161 
162  // cover the case where older versions of mkstemp() create the file using
163  // a mode of 666.
164  mode_t original_mode = umask(077);
165 
166  // Make and open (an atomic operation) the temporary file. Then reset the umask
167  int fd = mkstemp(&temp_file[0]);
168  umask(original_mode);
169 
170  if (fd == -1)
171  throw BESInternalError("Failed to open the temporary file: " + temp_file_name, __FILE__, __LINE__);
172 
173  // transform the OPeNDAP DataDDS to the geotiff file
174  BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - transforming into temporary file " << &temp_file[0] << endl);
175 
176  try {
177  // Handle *functional* constraint expressions specially
178  if (bdds->get_ce().function_clauses()) {
179  BESDEBUG("fonc", "processing a functional constraint clause(s)." << endl);
180  DataDDS *tmp_dds = bdds->get_ce().eval_function_clauses(*dds);
181  bdds->set_dds(tmp_dds);
182  delete dds;
183  dds = tmp_dds;
184  }
185  else {
186  // Iterate through the variables in the DataDDS and read
187  // in the data if the variable has the send flag set.
188  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
189  if ((*i)->send_p()) {
190  (*i)->intern_data(bdds->get_ce(), *dds);
191  }
192  }
193  }
194  }
195  catch (Error &e) {
196  throw BESInternalError("Failed to read data: " + e.get_error_message(), __FILE__, __LINE__);
197  }
198  catch (...) {
199  throw BESInternalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
200  }
201 
202  try {
203  FONgTransform ft(dds, bdds->get_ce(), &temp_file[0]);
204 
205  // transform() opens the temporary file, dumps data to it and closes it.
207 
208  BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - transmitting temp file " << &temp_file[0] << endl );
209 
210  JPEG2000Transmitter::return_temp_stream(&temp_file[0], strm);
211  }
212  catch (BESError &e) {
213  close(fd);
214  (void) unlink(&temp_file[0]);
215  throw;
216  }
217  catch (Error &e) {
218  close(fd);
219  (void) unlink(&temp_file[0]);
220  throw BESInternalError(e.get_error_message(), __FILE__, __LINE__);
221  }
222 
223  catch (...) {
224  close(fd);
225  (void) unlink(&temp_file[0]);
226  throw BESInternalError("Fileout GeoTiff, was not able to transform to jp2, unknown error", __FILE__, __LINE__);
227  }
228 
229  close(fd);
230  (void) unlink(&temp_file[0]);
231 
232  BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - done transmitting to jp2" << endl);
233 }
234 
244 void JPEG2000Transmitter::return_temp_stream(const string &filename, ostream &strm)
245 {
246  ifstream os;
247  os.open(filename.c_str(), ios::binary | ios::in);
248  if (!os)
249  throw BESInternalError("Cannot connect to data source", __FILE__, __LINE__);
250 
251  char block[4096];
252  os.read(block, sizeof block);
253  int nbytes = os.gcount();
254  if (nbytes == 0) {
255  os.close();
256  throw BESInternalError("Internal server error, got zero count on stream buffer.", __FILE__, __LINE__);
257  }
258 
259  bool found = false;
260  string context = "transmit_protocol";
261  string protocol = BESContextManager::TheManager()->get_context(context, found);
262  if (protocol == "HTTP") {
263  strm << "HTTP/1.0 200 OK\n";
264  strm << "Content-type: application/octet-stream\n";
265  strm << "Content-Description: " << "BES dataset" << "\n";
266  strm << "Content-Disposition: filename=" << filename << ".jp2;\n\n";
267  strm << flush;
268  }
269  strm.write(block, nbytes);
270 
271  while (os) {
272  os.read(block, sizeof block);
273  nbytes = os.gcount();
274  strm.write(block, nbytes);
275  }
276 
277  os.close();
278 }
279 
#define DATA_SERVICE
Definition: BESDapNames.h:71
void set_dds(DataDDS *ddsIn)
Set the response object's DDS.
exception thrown if inernal error encountered
#define JPEG2000_GCS
virtual bool add_method(string method_name, p_transmitter trans_method)
virtual string get_context(const string &name, bool &found)
retrieve the value of the specified context from the BES
static class NCMLUtil overview
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
JPEG2000Transmitter()
Construct the JPEG2000Transmitter, adding it with name geotiff to be able to transmit a data response...
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.
#define JPEG2000_TEMP_DIR
static void send_data_as_jp2(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a netcdf file.
map< string, string > data
the map of string data that will be required for the current request.
Transformation object that converts an OPeNDAP DataDDS to a GeoTiff file.
Definition: FONgTransform.h:41
#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...
virtual void transform_to_jpeg2000()
Transforms the variables of the DataDDS to a JPEG2000 file.