OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
DDSLoader.cc
Go to the documentation of this file.
1 // 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 #include "config.h"
30 #include "DDSLoader.h"
31 
32 #include <sstream>
33 
34 #include <BESConstraintFuncs.h>
35 #include <BESContainerStorage.h>
37 #include <BESDapNames.h>
38 #include <BESDapResponse.h>
39 #include <BESDataDDSResponse.h>
41 #include <BESDDSResponse.h>
42 #include <BESDebug.h>
43 #include <BESInternalError.h>
44 #include <BESResponseHandler.h>
45 #include <BESResponseNames.h>
46 #include <BESRequestHandlerList.h>
47 #include <BESServiceRegistry.h>
48 #include <BESTextInfo.h>
49 #include <BESUtil.h>
50 #include <BESVersionInfo.h>
51 //#include <mime_util.h>
52 #include "NCMLDebug.h"
53 #include "NCMLUtil.h"
54 
55 using namespace std;
56 using namespace agg_util;
57 
58 // Rep Init
59 
60 /* static */
61 long DDSLoader::_gensymID = 0L;
62 
63 // Impl
64 
65 DDSLoader::DDSLoader(BESDataHandlerInterface& dhi) :
66  _dhi(dhi), /*d_saved_dhi(0),*/ _hijacked(false), _filename(""), _store(0), _containerSymbol(""), _origAction(""),
67  _origActionName(""), _origContainer(0), _origResponse(0)
68 {
69 }
70 
71 // WE ONLY COPY THE DHI! I got forced to impl this.
73  _dhi(proto._dhi), /*d_saved_dhi(0),*/ _hijacked(false), _filename(""), _store(0), _containerSymbol(""), _origAction(""),
74  _origActionName(""), _origContainer(0), _origResponse(0)
75 {
76 }
77 
78 DDSLoader&
80 {
81  BESDEBUG("ncml", "DDSLoader::operator=: " << endl);
82 
83  if (&rhs == this) {
84  return *this;
85  }
86 
87  // First cleanup any state
88 #if 1
89  // Old comment, written in the midst of fixing bug #2176...
90  // I removed this call because ensureClean() will call restoreDHI()
91  // and then we will call the BESDataHandlerInterface::clone() method
92  // which will take the DHI from the DDSLoader passed in and clone it.
93  // ...no sense setting it twice.
94  //
95  // After the fix...
96  // Added it back in because I think it might be used after all. In many
97  // (all?) cases, the rhs._dhi is the same object as this->_dhi, so the
98  // clone() method will never get called. However, the 'saved state' of
99  // the DHI instance might be needed. It's not needed for the current
100  // operations (no tests fail when it is removed), but future versions
101  // might make use of the saved state.
102  // jhrg 4/18/14
103  ensureClean();
104 #else
105  removeContainerFromStorage();
106 #endif
107 
108  // Now copy the dhi only, since
109  // we assume we'll be using this fresh.
110  // Normally we don't want to copy these
111  // but I got forced into it.
112  //
113  // Update. Fix for bug #2176. With clang-503.0.40 calling
114  // BESDataHandlerInterface::make_copy() was inexplicably nulling the 'data'
115  // map member of the DHI. This was happening because the two maps were the same
116  // because the two DHI instances were the same - that is the 'rhs._dhi' field
117  // and this' _dhi field were/are one and the same. I'm going to leave this
118  // test here even though the code in BESDataHandlerInterface has been fixed to
119  // test for this case - and new copy ctor and operator=() methods added.
120  // jhrg 4/18/14
121  if (&_dhi != &rhs._dhi)
122  _dhi.make_copy(rhs._dhi);
123 
124  return *this;
125 }
126 
128 {
129  ensureClean();
130 }
131 
132 auto_ptr<BESDapResponse> DDSLoader::load(const string& location, ResponseType type)
133 {
134  // We need to make the proper response object as well, since in this call the dhi is coming in with the
135  // response object for the original ncml request.
136  std::auto_ptr<BESDapResponse> response = makeResponseForType(type);
137  loadInto(location, type, response.get());
138  return response; // relinquish
139 }
140 
141 void DDSLoader::loadInto(const std::string& location, ResponseType type, BESDapResponse* pResponse)
142 {
143  VALID_PTR(pResponse);
145 
146  // Just be sure we're cleaned up before doing anything, in case the caller calls load again after exception
147  // and before dtor.
148  ensureClean();
149 
150  _filename = location;
151 
152  // Remember current state of dhi before we touch it -- _hijacked is now true!!
153  snapshotDHI();
154 
155  // Add a new symbol to the storage list and return container for it.
156  // We will remove this new container on the way out.
157  BESContainer* container = addNewContainerToStorage();
158 
159  // Take over the dhi
160  _dhi.container = container;
161  _dhi.response_handler->set_response_object(pResponse);
162 
163  // Choose the proper request type...
164  _dhi.action = getActionForType(type);
165  _dhi.action_name = getActionNameForType(type);
166 
167  // TODO mpj do we need to do these calls?
168  BESDEBUG("ncml", "about to set dap version to: " << pResponse->get_dap_client_protocol() << endl);
169  BESDEBUG("ncml", "about to set xml:base to: " << pResponse->get_request_xml_base() << endl);
170 
171  // Figure out which underlying type of response it is to get the DDS (or DataDDS via DDS super).
173  if (!pDDS) {
174  THROW_NCML_INTERNAL_ERROR("DDSLoader::load expected BESDDSResponse or BESDataDDSResponse but got neither!");
175  }
176  pDDS->set_request_xml_base(pResponse->get_request_xml_base());
177 
178 #if 0
179  // I think this should be removed. pDDSResponse was likely changed to pResponse
180  // and pDDS is the same object. The BES will set these for us.
181  // I took these out since they seem to have changed and I am not sure what the right thing to do is...
182  pDDS->set_dap_major( pDDSResponse->get_dds()->get_dap_major() );
183  pDDS->set_dap_minor( pDDSResponse->get_dds()->get_dap_major() );
184 #endif
185 
186  // DO IT!
187  try {
188  BESDEBUG("ncml", "Before BESRequestHandlerList::TheList()->execute_current" << endl);
189  BESDEBUG("ncml", "Handler name: " << BESRequestHandlerList::TheList()->get_handler_names() << endl);
191  BESDEBUG("ncml", "After BESRequestHandlerList::TheList()->execute_current" << endl);
192  }
193  catch (BESError &e) {
194  cerr << "BESError: " << e.get_file() << ":" << e.get_line() << ": " << e.get_message();
195  }
196 
197  // Put back the dhi state we hijacked
198  restoreDHI();
199 
200  // Get rid of the container we added.
201  removeContainerFromStorage();
202 
203  _filename = "";
204 
205  // We should be clean here too.
206  ensureClean();
207 }
208 
209 void DDSLoader::cleanup() throw ()
210 {
211  ensureClean();
212 }
213 
215 DDSLoader::addNewContainerToStorage()
216 {
217  // Make sure we can find the storage
219  VALID_PTR(store_list);
220  BESContainerStorage* store = store_list->find_persistence("catalog");
221  if (!store) {
222  throw BESInternalError("couldn't find the catalog storage", __FILE__, __LINE__);
223  }
224 
225  // Make a new symbol from the ncml filename
226  // NCML_ASSERT_MSG(_dhi.container, "DDSLoader::addNewContainerToStorage(): null container!");
227  // string newSymbol = _dhi.container->get_symbolic_name() + "_location_" + _filename;
228  string newSymbol = getNextContainerName() + "__" + _filename;
229 
230  // this will throw an exception if the location isn't found in the
231  // catalog. Might want to catch this. Wish the add returned the
232  // container object created. Might want to change it.
233  store->add_container(newSymbol, _filename, "");
234 
235  // If we were successful, note the store location and symbol we added for removal later.
236  _store = store;
237  _containerSymbol = newSymbol;
238 
239  // Now look up the symbol we added
240  BESContainer *container = store->look_for(_containerSymbol);
241  if (!container) {
242  throw BESInternalError("couldn't find the container we just added:" + newSymbol, __FILE__, __LINE__);
243  }
244 
245  return container;
246 }
247 
248 void DDSLoader::removeContainerFromStorage() throw ()
249 {
250  // If we have non-null _store, we added the container symbol,
251  // so get rid of it
252  if (_store) {
253  try {
254  // This should go through, but if there's an exception, we could unwind through the dtor,
255  // so make sure we don't.
256  _store->del_container(_containerSymbol);
257  }
258  catch (BESError& besErr) {
259  BESDEBUG("ncml",
260  "WARNING: tried to remove symbol " << _containerSymbol << " from singleton but unexpectedly it was not there." << endl);
261  }
262  _containerSymbol = "";
263  _store = 0;
264  }
265 }
266 
267 void DDSLoader::snapshotDHI()
268 {
270 
271  BESDEBUG( "ncml", "original dhi = " << _dhi << endl ) ;
272 
273  // Store off the container for the original ncml file call and replace with the new one
274  _origContainer = _dhi.container;
275  _origAction = _dhi.action;
276  _origActionName = _dhi.action_name;
277 
278  _origResponse = _dhi.response_handler->get_response_object();
279 
280  _hijacked = true;
281 }
282 
283 void DDSLoader::restoreDHI()
284 {
286 
287  // Make sure we have state before we go mucking
288  if (!_hijacked) {
289  return;
290  }
291 
292  // Restore saved state
293  _dhi.container = _origContainer;
294  _dhi.action = _origAction;
295  _dhi.action_name = _origActionName;
296 
297  _dhi.response_handler->set_response_object(_origResponse);
298 
299  BESDEBUG( "ncml", "restored dhi = " << _dhi << endl ) ;
300 
301  // clear our copy of saved state
302  _origAction = "";
303  _origActionName = "";
304  _origResponse = 0;
305  _origContainer = 0;
306  _filename = "";
307 
308  _hijacked = false;
309 }
310 
311 void DDSLoader::ensureClean() throw ()
312 {
313  // If we're still hijacked here, there was an exception in load, so clean
314  // up if needed.
315  if (_hijacked) {
316  restoreDHI();
317  }
318 
319  // Make sure we've removed the new symbol from the container list as well.
320  removeContainerFromStorage();
321 }
322 
323 /* static */
324 std::string DDSLoader::getNextContainerName()
325 {
326  static const string _sPrefix = "__DDSLoader_Container_ID_";
327  _gensymID++;
328  std::ostringstream oss;
329  oss << _sPrefix << (_gensymID);
330  return oss.str();
331 }
332 
333 std::auto_ptr<BESDapResponse> DDSLoader::makeResponseForType(ResponseType type)
334 {
335  if (type == eRT_RequestDDX) {
336  return auto_ptr<BESDapResponse>(new BESDDSResponse(new DDS(new BaseTypeFactory(), "virtual")));
337  }
338  else if (type == eRT_RequestDataDDS) {
339  return auto_ptr<BESDapResponse>(new BESDataDDSResponse(new DataDDS(new BaseTypeFactory(), "virtual")));
340  }
341  else {
342  THROW_NCML_INTERNAL_ERROR("DDSLoader::makeResponseForType() got unknown type!");
343  }
344 #if 0
345  return auto_ptr<BESDapResponse>(0);
346 #endif
347 }
348 
350 {
351  if (type == eRT_RequestDDX) {
352  return DDS_RESPONSE;
353  }
354  else if (type == eRT_RequestDataDDS) {
355  return DATA_RESPONSE;
356  }
357 
358  THROW_NCML_INTERNAL_ERROR("DDSLoader::getActionForType(): unknown type!");
359 }
360 
362 {
363  if (type == eRT_RequestDDX) {
364  return DDX_RESPONSE_STR;
365  }
366  else if (type == eRT_RequestDataDDS) {
367  return DATA_RESPONSE_STR;
368  }
369 
370  THROW_NCML_INTERNAL_ERROR("DDSLoader::getActionNameForType(): unknown type!");
371 }
372 
374 {
375  if (type == eRT_RequestDDX) {
376  return dynamic_cast<BESDDSResponse*>(pResponse);
377  }
378  else if (type == eRT_RequestDataDDS) {
379  return dynamic_cast<BESDataDDSResponse*>(pResponse);
380  }
381  else {
382  return false;
383  }
384 }
385 
static std::auto_ptr< BESDapResponse > makeResponseForType(ResponseType type)
Make a new response object for the requested type.
Definition: DDSLoader.cc:333
string get_dap_client_protocol() const
Return the dap version string sent by the client (e.g., the OLFS)
provides persistent storage for data storage information represented by a container.
ResponseType
For telling the loader what type of BESDapResponse to load and return.
Definition: DDSLoader.h:97
exception thrown if inernal error encountered
virtual BESContainerStorage * find_persistence(const string &persist_name)
find the persistence store with the given name
static std::string getActionNameForType(ResponseType type)
Convert the type in the action name in BESResponseNames.h.
Definition: DDSLoader.cc:361
Represents an OPeNDAP DDS DAP2 data object within the BES.
static bool checkResponseIsValidType(ResponseType type, BESDapResponse *pResponse)
Return whether the given response's type matches the given ResposneType.
Definition: DDSLoader.cc:373
virtual BESContainer * look_for(const string &sym_name)=0
looks for a container in this persistent store
STL namespace.
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
#define L
Definition: avltree.h:36
static std::string getActionForType(ResponseType type)
Convert the type into the action in BESResponseNames.h for the type.
Definition: DDSLoader.cc:349
static libdap::DDS * getDDSFromEitherResponse(BESDapResponse *response)
Return the DDS* for the given response object.
Definition: NCMLUtil.cc:282
virtual BESResponseObject * set_response_object(BESResponseObject *o)
replaces the current response object with the specified one, returning the current response object ...
void make_copy(const BESDataHandlerInterface &copy_from)
deprecated
virtual string get_file()
get the file name where the exception was thrown
Definition: BESError.h:102
virtual string get_message()
get the error message for this exception
Definition: BESError.h:94
DDSLoader & operator=(const DDSLoader &)
Definition: DDSLoader.cc:79
virtual BESResponseObject * get_response_object()
return the current response object
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
#define DATA_RESPONSE
Definition: BESDapNames.h:70
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:141
BESResponseHandler * response_handler
Provides a mechanism for accessing container information from different container stores registered w...
virtual void add_container(const string &sym_name, const string &real_name, const string &type)=0
adds a container with the provided information
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
string get_request_xml_base() const
Return the xml:base URL for this request.
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:127
Represents an OPeNDAP DAP response object within the BES.
#define THROW_NCML_INTERNAL_ERROR(msg)
Definition: NCMLDebug.h:61
static BESRequestHandlerList * TheList()
Structure storing information used by the BES to handle the request.
static BESContainerStorageList * TheList()
#define DDX_RESPONSE_STR
Definition: BESDapNames.h:68
virtual void execute_current(BESDataHandlerInterface &dhi)
Execute a single method for the current container that will fill in the response object rather than i...
#define VALID_PTR(ptr)
Definition: NCMLDebug.h:88
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
virtual bool del_container(const string &s_name)=0
removes a container with the given symbolic name
A container is something that holds data.
Definition: BESContainer.h:60
DDSLoader(BESDataHandlerInterface &dhi)
Create a loader that will hijack dhi on a load call, then restore it's state.
Definition: DDSLoader.cc:65
#define DATA_RESPONSE_STR
Definition: BESDapNames.h:73
string action
the response object requested, e.g.
#define DDS_RESPONSE
Definition: BESDapNames.h:60
BESContainer * container
pointer to current container in this interface
std::auto_ptr< BESDapResponse > load(const std::string &location, ResponseType type)
Load and return a new DDX or DataDDS structure for the local dataset referred to by location...
Definition: DDSLoader.cc:132
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:110
void cleanup()
restore dhi to clean state
Definition: DDSLoader.cc:209