bes  Updated for version 3.17.4
BESXMLInterface.cc
1 // BESXMLInterface.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <iostream>
34 #include <sstream>
35 
36 using std::endl;
37 using std::cout;
38 using std::stringstream;
39 
40 #include "BESXMLInterface.h"
41 #include "BESXMLCommand.h"
42 #include "BESXMLUtils.h"
43 #include "BESDataNames.h"
44 #include "BESDebug.h"
45 #include "BESLog.h"
46 #include "BESSyntaxUserError.h"
47 #include "BESReturnManager.h"
48 
49 BESXMLInterface::BESXMLInterface(const string &xml_doc, ostream *strm) :
50  BESBasicInterface(strm)
51 {
52  _dhi = &_base_dhi;
53  _dhi->data[DATA_REQUEST] = "xml document";
54  // NB: The xml_doc is only used one place in the BES and that's on
55  // line 108 in this file. We could make this a field of the object and
56  // cut down on the use of the map. The downside is that putting the
57  // xml in the map makes it accessible when we look at the DHI. jhrg 2/23/16
58  _dhi->data["XMLDoc"] = xml_doc;
59 }
60 
61 BESXMLInterface::~BESXMLInterface()
62 {
63  clean();
64 }
65 
66 int BESXMLInterface::execute_request(const string &from)
67 {
69 }
70 
74 {
76 }
77 
81 {
83 }
84 
88 {
89  BESDEBUG("bes", "Entering: " << __PRETTY_FUNCTION__ << endl);
90  BESDEBUG("besxml", "building request plan for xml document: " << endl << _dhi->data["XMLDoc"] << endl);
91 
92  if (BESLog::TheLog()->is_verbose()) {
93  *(BESLog::TheLog()) << _dhi->data[SERVER_PID] << " from " << _dhi->data[REQUEST_FROM] << "] building" << endl;
94  }
95 
96  // I do not know why, but uncommenting this macro breaks some tests
97  // on linux but not OSX (CentOS 6, Ubuntu 12 versus OSX 10.11) by
98  // causing some XML elements in DMR responses to be twiddled in the
99  // responses build on linux but not on OSX.
100  // LIBXML_TEST_VERSION
101 
102  xmlDoc *doc = NULL;
103  xmlNode *root_element = NULL;
104  xmlNode *current_node = NULL;
105 
106  try {
107  // set the default error function to my own
108  vector<string> parseerrors;
109  xmlSetGenericErrorFunc((void *) &parseerrors, BESXMLUtils::XMLErrorFunc);
110 
111  // XML_PARSE_NONET
112  doc = xmlReadMemory(_dhi->data["XMLDoc"].c_str(), _dhi->data["XMLDoc"].size(), "" /* base URL */,
113  NULL /* encoding */, XML_PARSE_NONET /* xmlParserOption */);
114 
115  if (doc == NULL) {
116  string err = "Problem parsing the request xml document:\n";
117  bool isfirst = true;
118  vector<string>::const_iterator i = parseerrors.begin();
119  vector<string>::const_iterator e = parseerrors.end();
120  for (; i != e; i++) {
121  if (!isfirst && (*i).compare(0, 6, "Entity") == 0) {
122  err += "\n";
123  }
124  err += (*i);
125  isfirst = false;
126  }
127  throw BESSyntaxUserError(err, __FILE__, __LINE__);
128  }
129 
130  // get the root element and make sure it exists and is called request
131  root_element = xmlDocGetRootElement(doc);
132  if (!root_element) {
133  string err = "There is no root element in the xml document";
134  throw BESSyntaxUserError(err, __FILE__, __LINE__);
135  }
136 
137  string root_name;
138  string root_val;
139  map<string, string> props;
140  BESXMLUtils::GetNodeInfo(root_element, root_name, root_val, props);
141  if (root_name != "request") {
142  string err = (string) "The root element should be a request element, " + "name is "
143  + (char *) root_element->name;
144  throw BESSyntaxUserError(err, __FILE__, __LINE__);
145  }
146  if (root_val != "") {
147  string err = (string) "The request element must not contain a value, " + root_val;
148  throw BESSyntaxUserError(err, __FILE__, __LINE__);
149  }
150 
151  // there should be a request id property with one value.
152  string &reqId = props[REQUEST_ID];
153  if (reqId.empty()) {
154  string err = (string) "request id value empty";
155  throw BESSyntaxUserError(err, __FILE__, __LINE__);
156  }
157  _dhi->data[REQUEST_ID] = reqId;
158 
159  BESDEBUG("besxml", "request id = " << _dhi->data[REQUEST_ID] << endl);
160 
161  // iterate through the children of the request element. Each child is an
162  // individual command.
163  bool has_response = false;
164  current_node = root_element->children;
165 
166  while (current_node) {
167  if (current_node->type == XML_ELEMENT_NODE) {
168  // given the name of this node we should be able to find a
169  // BESXMLCommand object
170  string node_name = (char *) current_node->name;
171 
172  p_xmlcmd_builder bldr = BESXMLCommand::find_command(node_name);
173  if (bldr) {
174  BESXMLCommand *current_cmd = bldr(_base_dhi);
175  if (!current_cmd) {
176  string err = (string) "Failed to build command object for " + node_name;
177  throw BESInternalError(err, __FILE__, __LINE__);
178  }
179 
180  // push this new command to the back of the list
181  _cmd_list.push_back(current_cmd);
182 
183  // only one of the commands can build a response. If more
184  // than one builds a response, throw an error
185  bool cmd_has_response = current_cmd->has_response();
186  if (has_response && cmd_has_response) {
187  string err = "Multiple responses not allowed";
188  throw BESSyntaxUserError(err, __FILE__, __LINE__);
189  }
190  has_response = cmd_has_response;
191 
192  // parse the request given the current node
193  BESDEBUG("besxml", "parse request using " << node_name << endl);
194  current_cmd->parse_request(current_node);
195 
196  BESDataHandlerInterface &current_dhi = current_cmd->get_dhi();
197 
198  BESDEBUG("besxml", node_name << " parsed request, dhi = " << current_dhi << endl);
199 
200  string returnAs = current_dhi.data[RETURN_CMD];
201  if (returnAs != "") {
202  BESDEBUG("xml", "Finding transmitter: " << returnAs << " ... " << endl);
203  BESTransmitter *transmitter = BESReturnManager::TheManager()->find_transmitter(returnAs);
204  if (!transmitter) {
205  string s = (string) "Unable to find transmitter " + returnAs;
206  throw BESSyntaxUserError(s, __FILE__, __LINE__);
207  }
208  BESDEBUG("xml", "OK" << endl);
209  }
210  }
211  else {
212  string err = (string) "Unable to find command for " + node_name;
213  throw BESSyntaxUserError(err, __FILE__, __LINE__);
214  }
215  }
216  current_node = current_node->next;
217  }
218  }
219  catch (... /* BESError &e; changed 7/1/15 jhrg */) {
220  xmlFreeDoc(doc);
221  xmlCleanupParser();
222  throw /*e*/;
223  }
224 
225  xmlFreeDoc(doc);
226 
227  // Removed since the docs indicate it's not needed and it might be
228  // contributing to memory issues flagged by valgrind. 2/25/09 jhrg
229  //
230  // Added this back in. It seems to the the cause of BES-40 - where
231  // When certain tests are run, the order of <Dimension..> elements
232  // in a DMR for a server function result is different when the BESDEBUG
233  // output is on versus when it is not. This was true only when the
234  // BESDEBUG context was 'besxml' or timing,' which lead me here.
235  // Making this call removes the errant behavior. I've run tests using
236  // valgrind and I see no memory problems from this call. jhrg 9/25/15
237  xmlCleanupParser();
238 
239  BESDEBUG("besxml", "Done building request plan" << endl);
240 
242 }
243 
247 {
248  vector<BESXMLCommand *>::iterator i = _cmd_list.begin();
249  vector<BESXMLCommand *>::iterator e = _cmd_list.end();
250  for (; i != e; i++) {
251  (*i)->prep_request();
252  _dhi = &(*i)->get_dhi();
254  }
255 }
256 
260 {
262 }
263 
267 {
268  string returnAs = _dhi->data[RETURN_CMD];
269  if (returnAs != "") {
270  BESDEBUG("xml", "Setting transmitter: " << returnAs << " ... " << endl);
271  _transmitter = BESReturnManager::TheManager()->find_transmitter(returnAs);
272  if (!_transmitter) {
273  string s = (string) "Unable to find transmitter " + returnAs;
274  throw BESSyntaxUserError(s, __FILE__, __LINE__);
275  }
276  BESDEBUG("xml", "OK" << endl);
277  }
278 
280 }
281 
287 {
288  vector<BESXMLCommand *>::iterator i = _cmd_list.begin();
289  vector<BESXMLCommand *>::iterator e = _cmd_list.end();
290  for (; i != e; i++) {
291  _dhi = &(*i)->get_dhi();
293  }
294 }
295 
312 {
313  vector<BESXMLCommand *>::iterator i = _cmd_list.begin();
314  vector<BESXMLCommand *>::iterator e = _cmd_list.end();
315  for (; i != e; i++) {
316  _dhi = &(*i)->get_dhi();
318  }
319 }
320 
324 {
325  vector<BESXMLCommand *>::iterator i = _cmd_list.begin();
326  vector<BESXMLCommand *>::iterator e = _cmd_list.end();
327  for (; i != e; i++) {
328  BESXMLCommand *cmd = *i;
329  _dhi = &cmd->get_dhi();
331  delete cmd;
332  }
333  _cmd_list.clear();
334 }
335 
342 void BESXMLInterface::dump(ostream &strm) const
343 {
344  strm << BESIndent::LMarg << "BESXMLInterface::dump - (" << (void *) this << ")" << endl;
345  BESIndent::Indent();
347  vector<BESXMLCommand *>::const_iterator i = _cmd_list.begin();
348  vector<BESXMLCommand *>::const_iterator e = _cmd_list.end();
349  for (; i != e; i++) {
350  BESXMLCommand *cmd = *i;
351  cmd->dump(strm);
352  }
353  BESIndent::UnIndent();
354 }
355 
virtual int execute_request(const string &from)
Override execute_request in order to register memory pool.
exception thrown if inernal error encountered
virtual void dump(ostream &strm) const
dumps information about this object
Entry point into BES using string command requests.
virtual void invoke_aggregation()
Invoke the aggregation server, if there is one.
static void GetNodeInfo(xmlNode *node, string &name, string &value, map< string, string > &props)
get the name, value if any, and any properties for the specified node
Definition: BESXMLUtils.cc:99
virtual void initialize()
Initialize the BES.
virtual void parse_request(xmlNode *node)=0
Parse the XML request document begining at the given node.
virtual void log_status()
Log the status of the request to the BESLog file.
virtual BESDataHandlerInterface & get_dhi()
Return the current BESDataHandlerInterface.
Definition: BESXMLCommand.h:91
virtual void transmit_data()
Transmit the response object.
virtual void build_data_request_plan()
Build the data request plan using the BESCmdParser.
error thrown if there is a user syntax error in the request or any other user error ...
virtual void build_data_request_plan()
Build the data request plan using the BESCmdParser.
virtual void execute_data_request_plan()
Execute the data request plan.
static void XMLErrorFunc(void *context, const char *msg,...)
error function used by libxml2 to report errors
Definition: BESXMLUtils.cc:46
virtual void log_status()
Log the status of the request to the BESLog file.
virtual void clean()
Clean up after the request is completed.
virtual void report_request()
Report the request and status of the request to BESReporterList::TheList()
virtual void invoke_aggregation()
Invoke the aggregation server, if there is one.
virtual void dump(ostream &strm) const
dumps information about this object
virtual void report_request()
Report the request and status of the request.
virtual void validate_data_request()
Validate the incoming request information.
Structure storing information used by the BES to handle the request.
map< string, string > data
the map of string data that will be required for the current request.
virtual void transmit_data()
Transmit the response object.
virtual void validate_data_request()
Validate the incoming request information.
virtual void execute_data_request_plan()
Execute the data request plan.
virtual void initialize()
Initialize the BES.
virtual void clean()
Clean up after the request is completed.
virtual bool has_response()=0
Has a response handler been created given the request document?
virtual int execute_request(const string &from)
Override execute_request in order to register memory pool.
static p_xmlcmd_builder find_command(const string &cmd_str)
Find the BESXMLCommand creation function with the given name.
virtual void dump(ostream &strm) const
dumps information about this object