bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
DDSLoader.cc
1
2// This file is part of the "NcML Module" project, a BES module designed
3// to allow NcML files to be used to be used as a wrapper to add
4// AIS to existing datasets of any format.
5//
6// Copyright (c) 2009 OPeNDAP, Inc.
7// Author: Michael Johnson <m.johnson@opendap.org>
8//
9// For more information, please also see the main website: http://opendap.org/
10//
11// This library is free software; you can redistribute it and/or
12// modify it under the terms of the GNU Lesser General Public
13// License as published by the Free Software Foundation; either
14// version 2.1 of the License, or (at your option) any later version.
15//
16// This library is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19// Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public
22// License along with this library; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24//
25// Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26//
27// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29
30#include "config.h"
31
32#include <sstream>
33#include <algorithm>
34
35#include <libdap/DataDDS.h>
36
37#include <BESConstraintFuncs.h>
38#include <BESContainerStorage.h>
39#include <BESContainerStorageList.h>
40#include <BESDapNames.h>
41#include <BESDapResponse.h>
42#include <BESDataDDSResponse.h>
43#include <BESDataHandlerInterface.h>
44#include <BESDDSResponse.h>
45#include <BESStopWatch.h>
46#include <BESInternalError.h>
47#include <BESResponseHandler.h>
48#include <BESResponseNames.h>
49#include <BESRequestHandlerList.h>
50#include <BESServiceRegistry.h>
51#include <BESTextInfo.h>
52#include <BESUtil.h>
53#include <BESVersionInfo.h>
54
55#include <BESDebug.h>
56#include <BESLog.h>
57
58#include "DDSLoader.h"
59#include "NCMLDebug.h"
60#include "NCMLUtil.h"
61
62using namespace std;
63using namespace agg_util;
64using namespace libdap;
65
66#define prolog std::string("DDSLoader::").append(__func__).append("() - ")
67
68// Rep Init
69
70/* static */
71long DDSLoader::_gensymID = 0L;
72
73// Impl
74
76 _dhi(dhi), /*d_saved_dhi(0),*/_hijacked(false), _filename(""), _store(0), _containerSymbol(""), _origAction(""), _origActionName(
77 ""), _origContainer(0), _origResponse(0)
78{
79}
80
81// WE ONLY COPY THE DHI! I got forced to impl this.
82DDSLoader::DDSLoader(const DDSLoader& proto) :
83 _dhi(proto._dhi), /*d_saved_dhi(0),*/_hijacked(false), _filename(""), _store(0), _containerSymbol(""), _origAction(
84 ""), _origActionName(""), _origContainer(0), _origResponse(0)
85{
86}
87
89DDSLoader::operator=(const DDSLoader& rhs)
90{
91 BESDEBUG("ncml", "DDSLoader::operator=: " << endl);
92
93 if (&rhs == this) {
94 return *this;
95 }
96
97 // First cleanup any state
98
99 // Old comment, written in the midst of fixing bug #2176...
100 // I removed this call because ensureClean() will call restoreDHI()
101 // and then we will call the BESDataHandlerInterface::clone() method
102 // which will take the DHI from the DDSLoader passed in and clone it.
103 // ...no sense setting it twice.
104 //
105 // After the fix...
106 // Added it back in because I think it might be used after all. In many
107 // (all?) cases, the rhs._dhi is the same object as this->_dhi, so the
108 // clone() method will never get called. However, the 'saved state' of
109 // the DHI instance might be needed. It's not needed for the current
110 // operations (no tests fail when it is removed), but future versions
111 // might make use of the saved state.
112 // jhrg 4/18/14
113 ensureClean();
114
115 // Now copy the dhi only, since
116 // we assume we'll be using this fresh.
117 // Normally we don't want to copy these
118 // but I got forced into it.
119 //
120 // Update. Fix for bug #2176. With clang-503.0.40 calling
121 // BESDataHandlerInterface::make_copy() was inexplicably nulling the 'data'
122 // map member of the DHI. This was happening because the two maps were the same
123 // because the two DHI instances were the same - that is the 'rhs._dhi' field
124 // and this' _dhi field were/are one and the same. I'm going to leave this
125 // test here even though the code in BESDataHandlerInterface has been fixed to
126 // test for this case - and new copy ctor and operator=() methods added.
127 // jhrg 4/18/14
128 if (&_dhi != &rhs._dhi) _dhi.make_copy(rhs._dhi);
129
130 return *this;
131}
132
134{
135 ensureClean();
136}
137
138#if 0
139// Never used. 10/16/15 jhrg
140unique_ptr<BESDapResponse> DDSLoader::load(const string& location, ResponseType type)
141{
142 // We need to make the proper response object as well, since in this call the dhi is coming in with the
143 // response object for the original ncml request.
144 std::unique_ptr<BESDapResponse> response = makeResponseForType(type);
145 loadInto(location, type, response.get());
146 return response; // relinquish
147}
148#endif
149
150void DDSLoader::loadInto(const std::string& location, ResponseType type, BESDapResponse* pResponse)
151{
152 VALID_PTR(pResponse);
153 VALID_PTR(_dhi.response_handler);
154
155 // Just be sure we're cleaned up before doing anything, in case the caller calls load again after exception
156 // and before dtor.
157 ensureClean();
158
159 _filename = location;
160
161 // Remember current state of dhi before we touch it -- _hijacked is now true!!
162 snapshotDHI();
163
164#if 0
165 BESContainer *container = nullptr;
166
167 try {
168 // Add a new symbol to the storage list and return container for it.
169 // We will remove this new container on the way out.
170 //
171 // HK-474: If there is no handler configured to read the dataset (based on the values
172 // of the various TypeMatch parameters), code nested inside this call with throw a
173 // BESInternalError and the 'unwinding' of the containers will fail with some odd
174 // ramifications, including segfaults and no message to the user/client. jhrg 11/13/19
175 container = addNewContainerToStorage();
176 }
177 catch (BESError &e) {
178 // Get rid of the container we added.
179 removeContainerFromStorage();
180
181 // Put back the dhi state we hijacked
182 restoreDHI();
183
184 throw e;
185 }
186#endif
187
188 try {
189 // Add a new symbol to the storage list and return container for it.
190 // We will remove this new container on the way out.
191 //
192 // HK-474: If there is no handler configured to read the dataset (based on the values
193 // of the various TypeMatch parameters), code nested inside this call with throw a
194 // BESInternalError and the 'unwinding' of the containers will fail with some odd
195 // ramifications, including segfaults and no message to the user/client. jhrg 11/13/19
196 BESContainer *container = addNewContainerToStorage();
197
198 // Take over the dhi
199 // Container is allocated using ptr_duplicate. Must free existing container. See RestoreDHI. jhrg 6/19/19
200 _dhi.container = container;
201 _dhi.response_handler->set_response_object(pResponse);
202
203 // Choose the proper request type...
204 _dhi.action = getActionForType(type);
205 _dhi.action_name = getActionNameForType(type);
206
207 // Figure out which underlying type of response it is to get the DDS (or DataDDS via DDS super).
209 if (!pDDS) {
210 THROW_NCML_INTERNAL_ERROR("DDSLoader::load expected BESDDSResponse or BESDataDDSResponse but got neither!");
211 }
212 pDDS->set_request_xml_base(pResponse->get_request_xml_base());
213
214 // DO IT!
215
216 BESDEBUG("ncml", "Before BESRequestHandlerList::TheList()->execute_current" << endl);
217 BESDEBUG("ncml", "Handler name: " << BESRequestHandlerList::TheList()->get_handler_names() << endl);
218
219 BESRequestHandlerList::TheList()->execute_current(_dhi);
220
221 // Some NcML operations like rename/add attributes need to have attributes in the data access.
222 // So we need to check if the attributes are added by the underneath handlers.
223 // If not, it will be added here. KY 10/30/19
224 if(type == eRT_RequestDataDDS) {
225
226 BESResponseObject *response = _dhi.response_handler->get_response_object();
227 BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *> (response);
228 if (!bdds)
229 throw BESInternalError("cast error", __FILE__, __LINE__);
230
231 if(bdds->get_ia_flag() == false) {
232 BESDEBUG("ncml", "Underneath handler "<< _dhi.container->get_container_type() << " call add_attributes() " << endl);
233 BESRequestHandler *besRH = BESRequestHandlerList::TheList()->find_handler(_dhi.container->get_container_type());
234 besRH->add_attributes(_dhi);
235 }
236 }
237
238 BESDEBUG("ncml", "After BESRequestHandlerList::TheList()->execute_current.\n");
239
240 _filename = "";
241
242 ensureClean();
243 }
244 catch (BESError &e) {
245 // We should be clean here too.
246 ensureClean();
247
248 throw e;
249 }
250}
251
253{
254 ensureClean();
255}
256
257bool is_url(const std::string &location) {
258 std::string http("http://");
259 std::string https("https://");
260
261 // case insensitive check
262 std::string tip = location.substr(0,http.size());
263 std::transform(tip.begin(), tip.end(), tip.begin(), ::tolower);
264 bool result = http == tip; // http.compare(tip)==0;
265
266 // case insensitive check
267 tip = location.substr(0,https.size());
268 std::transform(tip.begin(), tip.end(), tip.begin(), ::tolower);
269
270 result = result || http == tip; //http.compare(tip)==0;
271
272 return result;
273}
274
276DDSLoader::addNewContainerToStorage()
277{
278 // Make sure we can find the storage
279 BESContainerStorageList *store_list = BESContainerStorageList::TheList();
280 VALID_PTR(store_list);
281
282 // Check for URL in the _filename, 'cause that means gateway!
283 BESContainerStorage* store;
284 if(is_url(_filename)){
285 BESDEBUG("ncml", __func__ << "() - GATEWAY CONTAINER!" << endl);
286 store = store_list->find_persistence("gateway");
287 }
288 else {
289 store = store_list->find_persistence("catalog");
290 }
291 if (!store) {
292 throw BESInternalError("couldn't find the catalog storage", __FILE__, __LINE__);
293 }
294
295 // Make a new symbol from the ncml filename
296 string newSymbol = getNextContainerName() + "__" + _filename;
297
298 // this will throw an exception if the location isn't found in the
299 // catalog. Might want to catch this. Wish the add returned the
300 // container object created. Might want to change it.
301 //
302 // HK-474 This is the crux of the problem.
303 store->add_container(newSymbol, _filename, "");
304
305 // If we were successful, note the store location and symbol we added for removal later.
306 _store = store;
307 _containerSymbol = newSymbol;
308
309 // Now look up the symbol we added
310 BESContainer *container = store->look_for(_containerSymbol);
311 if (!container) {
312 throw BESInternalError("couldn't find the container we just added:" + newSymbol, __FILE__, __LINE__);
313 }
314
315 return container;
316}
317
318void DDSLoader::removeContainerFromStorage()
319{
320 // If we have non-null _store, we added the container symbol,
321 // so get rid of it
322 if (_store) {
323 try {
324 // This should go through, but if there's an exception, we could unwind through the dtor,
325 // so make sure we don't.
326 _store->del_container(_containerSymbol);
327 }
328 catch (BESError& besErr) {
329 ERROR_LOG("WARNING: tried to remove symbol " + _containerSymbol
330 + " from singleton but unexpectedly it was not there.\n");
331 }
332 _containerSymbol = "";
333 _store = 0;
334 }
335}
336
337void DDSLoader::snapshotDHI()
338{
339 VALID_PTR(_dhi.response_handler);
340
341 BESDEBUG( "ncml", "DDSLoader::snapshotDHI() - Taking snapshot of DataHAndlerInterface for (action: " << _dhi.action << " action_name: " << _dhi.action_name << ")" << endl );
342 BESDEBUG( "ncml_verbose", "original dhi = " << _dhi << endl );
343
344 // Store off the container for the original ncml file call and replace with the new one
345 _origContainer = _dhi.container;
346 _dhi.container = 0; // Mark the container so that we do not call release() more than once. jhrg 11/14/19
347 _origAction = _dhi.action;
348 _origActionName = _dhi.action_name;
349
350 _origResponse = _dhi.response_handler->get_response_object();
351
352 BESDEBUG( "ncml", "DDSLoader::snapshotDHI() - Replaced with DataHAndlerInterface for (action: " << _dhi.action << " action_name: " << _dhi.action_name << ")" << endl );
353
354 _hijacked = true;
355}
356
357void DDSLoader::restoreDHI()
358{
359 VALID_PTR(_dhi.response_handler);
360
361 // Make sure we have state before we go mucking
362 if (!_hijacked) {
363 return;
364 }
365
366 // Before we overwrite the 'high jacked' DHI's container, call
367 // release(). If this is a simple file, it's no big deal to
368 // skip this (because the file is closed elsewhere). But, if the
369 // DHI is working with a _compressed_ file, it is a big deal
370 // because this is the call that closes the cached uncompressed
371 // file and frees the lock. This was the bug associated with
372 // ticket HR-64. jhrg 10/16/15
373 // Only release the container if it is not null. This was happening
374 // because DDSLoader:loadInto() was failing in mid-process and the
375 // DHI had been hijacked but the new container was not set. See HK-474.
376 // jhrg 11/14/19
377 if (_dhi.container) _dhi.container->release();
378 // Leak. Allocated locally by addNewContainerToStorage() in loadInto(). jhrg 6/19/19
379 delete _dhi.container;
380
381 // Restore saved state
382 _dhi.container = _origContainer;
383 _dhi.action = _origAction;
384 _dhi.action_name = _origActionName;
385
386 _dhi.response_handler->set_response_object(_origResponse);
387
388 BESDEBUG( "ncml", "DDSLoader::restoreDHI() - Restored of DataHAndlerInterface for (action: " << _dhi.action << " action_name: " << _dhi.action_name << ")" << endl );
389
390 BESDEBUG( "ncml_verbose", "restored dhi = " << _dhi << endl );
391
392 // clear our copy of saved state
393 _origAction = "";
394 _origActionName = "";
395 _origResponse = 0;
396 _origContainer = 0;
397 _filename = "";
398
399 _hijacked = false;
400}
401
405void DDSLoader::ensureClean()
406{
407 // If we're still hijacked here, there was an exception in load, so clean
408 // up if needed.
409 if (_hijacked) {
410 restoreDHI();
411 }
412
413 // Make sure we've removed the new symbol from the container list as well.
414 removeContainerFromStorage();
415}
416
417/* static */
418std::string DDSLoader::getNextContainerName()
419{
420 static const string _sPrefix = "__DDSLoader_Container_ID_";
421 _gensymID++;
422 std::ostringstream oss;
423 oss << _sPrefix << (_gensymID);
424 return oss.str();
425}
426
427unique_ptr<BESDapResponse> DDSLoader::makeResponseForType(ResponseType type)
428{
429 if (type == eRT_RequestDDX) {
430 // The BaseTypeFactory is leaked. jhrg 6/19/19
431 return unique_ptr<BESDapResponse>(new BESDDSResponse(new DDS(nullptr /*new BaseTypeFactory()*/, "virtual")));
432 }
433 else if (type == eRT_RequestDataDDS) {
434 // Leak fix jhrg 6/19/19
435 return unique_ptr<BESDapResponse>(new BESDataDDSResponse(new DDS(nullptr /*new BaseTypeFactory()*/, "virtual")));
436 }
437 else {
438 THROW_NCML_INTERNAL_ERROR("DDSLoader::makeResponseForType() got unknown type!");
439 }
440}
441
443{
444 if (type == eRT_RequestDDX) {
445 return DDS_RESPONSE;
446 }
447 else if (type == eRT_RequestDataDDS) {
448 return DATA_RESPONSE;
449 }
450
451 THROW_NCML_INTERNAL_ERROR("DDSLoader::getActionForType(): unknown type!");
452}
453
455{
456 if (type == eRT_RequestDDX) {
457 return DDX_RESPONSE_STR;
458 }
459 else if (type == eRT_RequestDataDDS) {
460 return DATA_RESPONSE_STR;
461 }
462
463 THROW_NCML_INTERNAL_ERROR("DDSLoader::getActionNameForType(): unknown type!");
464}
465
467{
468 if (type == eRT_RequestDDX) {
469 return dynamic_cast<BESDDSResponse*>(pResponse);
470 }
471 else if (type == eRT_RequestDataDDS) {
472 return dynamic_cast<BESDataDDSResponse*>(pResponse);
473 }
474 else {
475 return false;
476 }
477}
478
Provides a mechanism for accessing container information from different container stores registered w...
provides persistent storage for data storage information represented by a container.
virtual void add_container(const std::string &sym_name, const std::string &real_name, const std::string &type)=0
adds a container with the provided information
virtual BESContainer * look_for(const std::string &sym_name)=0
looks for a container in this persistent store
A container is something that holds data. E.G., a netcdf file or a database entry.
Holds a DDS object within the BES.
Represents an OPeNDAP DAP response object within the BES.
std::string get_request_xml_base() const
Return the xml:base URL for this request.
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
Structure storing information used by the BES to handle the request.
Base exception class for the BES with basic string message.
Definition BESError.h:66
exception thrown if internal error encountered
Represents a specific data type request handler.
Abstract base class representing a specific set of information in response to a request to the BES.
static std::unique_ptr< BESDapResponse > makeResponseForType(ResponseType type)
Definition DDSLoader.cc:427
void loadInto(const std::string &location, ResponseType type, BESDapResponse *pResponse)
Load a DDX or DataDDS response into the given pResponse object, which must be non-null.
Definition DDSLoader.cc:150
DDSLoader(BESDataHandlerInterface &dhi)
Create a loader that will hijack dhi on a load call, then restore it's state.
Definition DDSLoader.cc:75
static bool checkResponseIsValidType(ResponseType type, BESDapResponse *pResponse)
Definition DDSLoader.cc:466
void cleanup()
restore dhi to clean state
Definition DDSLoader.cc:252
static std::string getActionForType(ResponseType type)
Definition DDSLoader.cc:442
static std::string getActionNameForType(ResponseType type)
Definition DDSLoader.cc:454
virtual ~DDSLoader()
Dtor restores the state of dhi Restores the state of the dhi to what it was when object if it is stil...
Definition DDSLoader.cc:133
static libdap::DDS * getDDSFromEitherResponse(BESDapResponse *response)
Definition NCMLUtil.cc:356
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
utility class for the HTTP catalog module
Definition TheBESKeys.h:51