bes Updated for version 3.20.10
JPEG2000Transmitter.cc
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 <libdap/DataDDS.h>
39#include <libdap/BaseType.h>
40#include <libdap/escaping.h>
41
42using namespace libdap;
43
44#include "JPEG2000Transmitter.h"
45#include "FONgTransform.h"
46
47#include <BESUtil.h>
48#include <BESInternalError.h>
49#include <BESDapError.h>
50#include <BESContextManager.h>
51#include <BESDataDDSResponse.h>
52#include <BESDapNames.h>
53#include <BESDataNames.h>
54#include <BESDebug.h>
55#include <TempFile.h>
56#include <DapFunctionUtils.h>
57
58#include <TheBESKeys.h>
59
60#define JPEG2000_TEMP_DIR "/tmp"
61#define JPEG2000_GCS "WGS84"
62
63string JPEG2000Transmitter::temp_dir;
64string JPEG2000Transmitter::default_gcs;
65
83{
84 // DATA_SERVICE == "dods"
85 add_method(DATA_SERVICE, JPEG2000Transmitter::send_data_as_jp2);
86
87 if (JPEG2000Transmitter::temp_dir.empty()) {
88 // Where is the temp directory for creating these files
89 bool found = false;
90 string key = "JPEG2000.Tempdir";
91 TheBESKeys::TheKeys()->get_value(key, JPEG2000Transmitter::temp_dir, found);
92 if (!found || JPEG2000Transmitter::temp_dir.empty()) {
93 JPEG2000Transmitter::temp_dir = JPEG2000_TEMP_DIR;
94 }
95 string::size_type len = JPEG2000Transmitter::temp_dir.length();
96 if (JPEG2000Transmitter::temp_dir[len - 1] == '/') {
97 JPEG2000Transmitter::temp_dir = JPEG2000Transmitter::temp_dir.substr(0, len - 1);
98 }
99 }
100
101 if (JPEG2000Transmitter::default_gcs.empty()) {
102 // Use what as the default Geographic coordinate system?
103 bool found = false;
104 string key = "JPEG2000.Default_GCS";
105 TheBESKeys::TheKeys()->get_value(key, JPEG2000Transmitter::default_gcs, found);
106 if (!found || JPEG2000Transmitter::default_gcs.empty()) {
107 JPEG2000Transmitter::default_gcs = JPEG2000_GCS;
108 }
109 }
110}
111
128{
129 BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
130 if (!bdds) {
131 throw BESInternalError("cast error", __FILE__, __LINE__);
132 }
133
134 DDS *dds = bdds->get_dds();
135 if (!dds)
136 throw BESInternalError("No DataDDS has been created for transmit", __FILE__, __LINE__);
137
138 ostream &strm = dhi.get_output_stream();
139 if (!strm)
140 throw BESInternalError("Output stream is not set, cannot return as", __FILE__, __LINE__);
141
142 BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - parsing the constraint" << endl);
143
144 // ticket 1248 jhrg 2/23/09
145 string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
146 try {
147 bdds->get_ce().parse_constraint(ce, *dds);
148 }
149 catch (Error &e) {
150 throw BESDapError("Failed to parse the constraint expression: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
151 }
152 catch (...) {
153 throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__, __LINE__);
154 }
155
156 // now we need to read the data
157 BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - reading data into DataDDS" << endl);
158
159 bes::TempFile temp_file(JPEG2000Transmitter::temp_dir + '/' + "jp2XXXXXX");
160#if 0
161 // Huh? Put the template for the temp file name in a char array. Use vector<char>
162 // to avoid using new/delete.
163 string temp_file_name = JPEG2000Transmitter::temp_dir + '/' + "jp2XXXXXX";
164 vector<char> temp_file(temp_file_name.length() + 1);
165 string::size_type len = temp_file_name.copy(&temp_file[0], temp_file_name.length());
166 temp_file[len] = '\0';
167
168 // cover the case where older versions of mkstemp() create the file using
169 // a mode of 666.
170 mode_t original_mode = umask(077);
171
172 // Make and open (an atomic operation) the temporary file. Then reset the umask
173 int fd = mkstemp(&temp_file[0]);
174 umask(original_mode);
175
176 if (fd == -1)
177 throw BESInternalError("Failed to open the temporary file: " + temp_file_name, __FILE__, __LINE__);
178#endif
179 // transform the OPeNDAP DataDDS to the geotiff file
180 BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - transforming into temporary file " << temp_file.get_name() << endl);
181
182 try {
183
184 // Handle *functional* constraint expressions specially
185 if (bdds->get_ce().function_clauses()) {
186 BESDEBUG("fong2", "processing a functional constraint clause(s)." << endl);
187 DDS *tmp_dds = bdds->get_ce().eval_function_clauses(*dds);
188 delete dds;
189 dds = tmp_dds;
190 bdds->set_dds(dds);
191
192 // This next step utilizes a well known function, promote_function_output_structures()
193 // to look for one or more top level Structures whose name indicates (by way of ending
194 // with "_uwrap") that their contents should be promoted (aka moved) to the top level.
195 // This is in support of a hack around the current API where server side functions
196 // may only return a single DAP object and not a collection of objects. The name suffix
197 // "_unwrap" is used as a signal from the function to the the various response
198 // builders and transmitters that the representation needs to be altered before
199 // transmission, and that in fact is what happens in our friend
200 // promote_function_output_structures()
201 promote_function_output_structures(dds);
202
203 }
204 else {
205 // Iterate through the variables in the DataDDS and read
206 // in the data if the variable has the send flag set.
207 for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
208 if ((*i)->send_p()) {
209 (*i)->intern_data(bdds->get_ce(), *dds);
210 }
211 }
212 }
213
214 }
215 catch (Error &e) {
216 throw BESDapError("Failed to read data: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
217 }
218 catch (BESError &e) {
219 throw;
220 }
221 catch (...) {
222 throw BESInternalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
223 }
224
225 try {
226 FONgTransform ft(dds, bdds->get_ce(), temp_file.get_name());
227
228 // Now that we are ready to start building the response data we
229 // cancel any pending timeout alarm according to the configuration.
231
232 // transform() opens the temporary file, dumps data to it and closes it.
234
235 BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - transmitting temp file " << temp_file.get_name() << endl );
236
237 JPEG2000Transmitter::return_temp_stream(temp_file.get_name(), strm);
238 }
239 catch (Error &e) {
240#if 0
241 close(fd);
242 (void) unlink(&temp_file[0]);
243#endif
244 throw BESDapError("Failed to transform data to JPEG2000: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
245 }
246 catch (BESError &e) {
247#if 0
248 close(fd);
249 (void) unlink(&temp_file[0]);
250#endif
251 throw;
252 }
253 catch (...) {
254#if 0
255 close(fd);
256 (void) unlink(&temp_file[0]);
257#endif
258 throw BESInternalError("Fileout GeoTiff, was not able to transform to JPEG2000, unknown error", __FILE__, __LINE__);
259 }
260
261#if 0
262 close(fd);
263 (void) unlink(&temp_file[0]);
264#endif
265
266 BESDEBUG("JPEG20002", "JPEG2000Transmitter::send_data - done transmitting to jp2" << endl);
267}
268
278void JPEG2000Transmitter::return_temp_stream(const string &filename, ostream &strm)
279{
280 ifstream os;
281 os.open(filename.c_str(), ios::binary | ios::in);
282 if (!os)
283 throw BESInternalError("Cannot connect to data source", __FILE__, __LINE__);
284
285 char block[4096];
286 os.read(block, sizeof block);
287 int nbytes = os.gcount();
288 if (nbytes == 0) {
289 os.close();
290 throw BESInternalError("Internal server error, got zero count on stream buffer.", __FILE__, __LINE__);
291 }
292
293 bool found = false;
294 string context = "transmit_protocol";
295 string protocol = BESContextManager::TheManager()->get_context(context, found);
296 if (protocol == "HTTP") {
297 strm << "HTTP/1.0 200 OK\n";
298 strm << "Content-type: application/octet-stream\n";
299 strm << "Content-Description: " << "BES dataset" << "\n";
300 strm << "Content-Disposition: filename=" << filename << ".jp2;\n\n";
301 strm << flush;
302 }
303 strm.write(block, nbytes);
304
305 while (os) {
306 os.read(block, sizeof block);
307 nbytes = os.gcount();
308 strm.write(block, nbytes);
309 }
310
311 os.close();
312}
313
virtual std::string get_context(const std::string &name, bool &found)
retrieve the value of the specified context from the BES
error object created from libdap error objects and can handle those errors
Definition: BESDapError.h:59
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
void set_dds(libdap::DDS *ddsIn)
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.
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
exception thrown if internal error encountered
Abstract base class representing a specific set of information in response to a request to the BES.
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:992
Transformation object that converts an OPeNDAP DataDDS to a GeoTiff file.
Definition: FONgTransform.h:42
virtual void transform_to_jpeg2000()
Transforms the variables of the DataDDS to a JPEG2000 file.
JPEG2000Transmitter()
Construct the JPEG2000Transmitter, adding it with name geotiff to be able to transmit a data response...
static void send_data_as_jp2(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a netcdf file.
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
Get a new temporary file.
Definition: TempFile.h:46
std::string get_name() const
Definition: TempFile.h:70