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
72using namespace ::libdap;
73
74#define MODULE "w10n"
75#define prolog string("W10nJsonTransmitter::").append(__func__).append("() - ")
76
77#define W10N_JSON_TEMP_DIR "/tmp"
78
79string 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
117void 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
137string 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
156string 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
168struct 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
322void 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.