bes  Updated for version 3.20.10
StandAloneClient.cc
1 // StandAloneClient.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) 2014 OPeNDAP, Inc.
7 //
8 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
9 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
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 // You can contact University Corporation for Atmospheric Research at
26 // 3080 Center Green Drive, Boulder, CO 80301
27 
28 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
29 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
30 //
31 // Authors:
32 //
33 // pwest Patrick West <pwest@ucar.edu>
34 // jgarcia Jose Garcia <jgarcia@ucar.edu>
35 // hyoklee Hyo-Kyung Lee <hyoklee@hdfgroup.org>
36 
37 #include "config.h"
38 
39 #include <cstdlib>
40 #include <iostream>
41 #include <fstream>
42 #include <sstream>
43 
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 
48 using std::cout;
49 using std::endl;
50 using std::cerr;
51 using std::ofstream;
52 using std::ios;
53 using std::flush;
54 using std::ostringstream;
55 using std::ostream;
56 using std::string;
57 using std::ifstream;
58 
59 #ifdef HAVE_LIBREADLINE
60 # if defined(HAVE_READLINE_READLINE_H)
61 # include <readline/readline.h>
62 # elif defined(HAVE_READLINE_H)
63 # include <readline.h>
64 # else /* !defined(HAVE_READLINE_H) */
65 extern "C" {
66  char *readline(const char *);
67 }
68 # endif /* !defined(HAVE_READLINE_H) */
69 char *cmdline = NULL;
70 #else /* !defined(HAVE_READLINE_READLINE_H) */
71 /* no readline */
72 #endif /* HAVE_LIBREADLINE */
73 
74 #ifdef HAVE_READLINE_HISTORY
75 # if defined(HAVE_READLINE_HISTORY_H)
76 # include <readline/history.h>
77 # elif defined(HAVE_HISTORY_H)
78 # include <history.h>
79 # else /* !defined(HAVE_HISTORY_H) */
80 extern "C" {
81  int add_history(const char *);
82  int write_history(const char *);
83  int read_history(const char *);
84 }
85 # endif /* defined(HAVE_READLINE_HISTORY_H) */
86 /* no history */
87 #endif /* HAVE_READLINE_HISTORY */
88 
89 
90 #define SIZE_COMMUNICATION_BUFFER (4096*4096)
91 
92 #include "BESXMLInterface.h"
93 #include "BESStopWatch.h"
94 #include "BESError.h"
95 #include "BESDebug.h"
96 
97 #include "StandAloneClient.h"
98 #include "CmdTranslation.h"
99 
100 StandAloneClient::~StandAloneClient()
101 {
102  if (_strmCreated && _strm) {
103  _strm->flush();
104  delete _strm;
105  _strm = nullptr;
106  }
107  else if (_strm) {
108  _strm->flush();
109  }
110 }
111 
128 void StandAloneClient::setOutput(ostream * strm, bool created)
129 {
130  if (_strmCreated && _strm) {
131  _strm->flush();
132  delete _strm;
133  }
134  else if (_strm) {
135  _strm->flush();
136  }
137  _strm = strm;
138  _strmCreated = created;
139 }
140 
153 {
154  string suppress = "suppress";
155  if (cmd.compare(0, suppress.length(), suppress) == 0) {
156  setOutput(nullptr, false);
157  return;
158  }
159 
160  string output = "output to";
161  if (cmd.compare(0, output.length(), output) == 0) {
162  string subcmd = cmd.substr(output.length() + 1);
163  string screen = "screen";
164  if (subcmd.compare(0, screen.length(), screen) == 0) {
165  setOutput(&cout, false);
166  }
167  else {
168  // subcmd is the name of the file - then semicolon
169  string file = subcmd.substr(0, subcmd.length() - 1);
170  auto *fstrm = new ofstream(file.c_str(), ios::app);
171  if (!(*fstrm)) {
172  delete fstrm;
173  cerr << "Unable to set client output to file " << file << endl;
174  }
175  else {
176  setOutput(fstrm, true);
177  }
178  }
179  return;
180  }
181 
182  // load commands from an input file and run them
183  string load = "load";
184  if (cmd.compare(0, load.length(), load) == 0) {
185  string file = cmd.substr(load.length() + 1, cmd.length() - load.length() - 2);
186  ifstream fstrm(file.c_str());
187  if (!fstrm) {
188  cerr << "Unable to load commands from file " << file << ": file does not exist or failed to open file"
189  << endl;
190  }
191  else {
192  executeCommands(fstrm, 1);
193  }
194 
195  return;
196  }
197 
198  cerr << "Improper client command " << cmd << endl;
199 }
200 
213 void StandAloneClient::executeCommand(const string & cmd, int repeat)
214 {
215  string client = "client";
216  if (cmd.compare(0, client.length(), client) == 0) {
217  executeClientCommand(cmd.substr(client.length() + 1));
218  }
219  else {
220  if (repeat < 1) repeat = 1;
221  for (int i = 0; i < repeat; i++) {
222  ostringstream *show_stream = nullptr;
223  if (CmdTranslation::is_show()) {
224  show_stream = new ostringstream;
225  }
226 
227  BESDEBUG( "standalone", "StandAloneClient::executeCommand sending: " << cmd << endl );
228 
229  BESStopWatch sw;
230  if (BESDebug::IsSet(TIMING_LOG_KEY)) sw.start("StandAloneClient::executeCommand");
231 
232  BESXMLInterface *interface = nullptr;
233  if (show_stream) {
234  interface = new BESXMLInterface(cmd, show_stream);
235  }
236  else {
237  interface = new BESXMLInterface(cmd, _strm);
238  }
239 
240  int status = interface->execute_request("standalone");
241 
242  *_strm << flush;
243 
244  // Put the call to finish() here beacuse we're not sending chunked responses back
245  // to a client over PPT. In the BESServerHandler.cc code, we must do that and hence,
246  // break up the call to finish() for the error and no-error cases.
247  status = interface->finish(status);
248 
249  if (status == 0) {
250  BESDEBUG("standalone", "StandAloneClient::executeCommand - executed successfully" << endl);
251  }
252  else {
253  // an error has occurred.
254  BESDEBUG("standalone", "StandAloneClient::executeCommand - error occurred" << endl);
255  switch (status) {
256  case BES_INTERNAL_FATAL_ERROR: {
257  cerr << "Status not OK, dispatcher returned value " << status << endl;
258  exit(1);
259  }
260 
261  case BES_INTERNAL_ERROR:
262  case BES_SYNTAX_USER_ERROR:
263  case BES_FORBIDDEN_ERROR:
264  case BES_NOT_FOUND_ERROR:
265  default:
266  break;
267  }
268  }
269 
270  delete interface;
271  interface = nullptr;
272 
273  if (show_stream) {
274  *(_strm) << show_stream->str() << endl;
275  delete show_stream;
276  show_stream = nullptr;
277  }
278 
279  _strm->flush();
280  }
281  }
282 }
283 
300 void StandAloneClient::executeCommands(const string &cmd_list, int repeat)
301 {
302  _isInteractive = true;
303  if (repeat < 1) repeat = 1;
304 
305  CmdTranslation::set_show(false);
306  try {
307  string doc = CmdTranslation::translate(cmd_list);
308  if (!doc.empty()) {
309  executeCommand(doc, repeat);
310  }
311  }
312  catch (BESError &e) {
313  CmdTranslation::set_show(false);
314  _isInteractive = false;
315  throw e;
316  }
317  CmdTranslation::set_show(false);
318  _isInteractive = false;
319 }
320 
339 void StandAloneClient::executeCommands(ifstream & istrm, int repeat)
340 {
341  _isInteractive = false;
342  if (repeat < 1) repeat = 1;
343  for (int i = 0; i < repeat; i++) {
344  istrm.clear();
345  istrm.seekg(0, ios::beg);
346  string cmd;
347  string line;
348  while (getline(istrm, line)) {
349  cmd += line;
350  }
351  this->executeCommand(cmd, 1);
352  }
353 }
354 
371 {
372  _isInteractive = true;
373 
374  cout << endl << endl << "Type 'exit' to exit the command line client and 'help' or '?' "
375  << "to display the help screen" << endl << endl;
376 
377  bool done = false;
378  while (!done) {
379  string message;
380  size_t len = this->readLine(message);
381  if ( /*len == -1 || */message == "exit" || message == "exit;") {
382  done = true;
383  }
384  else if (message == "help" || message == "help;" || message == "?") {
385  this->displayHelp();
386  }
387  else if (message.length() > 6 && message.substr(0, 6) == "client") {
388  this->executeCommand(message, 1);
389  }
390  else if (len != 0 && !message.empty()) {
391  CmdTranslation::set_show(false);
392  try {
393  string doc = CmdTranslation::translate(message);
394  if (!doc.empty()) {
395  this->executeCommand(doc, 1);
396  }
397  }
398  catch (BESError &e) {
399  CmdTranslation::set_show(false);
400  _isInteractive = false;
401  throw e;
402  }
403  CmdTranslation::set_show(false);
404  }
405  }
406  _isInteractive = false;
407 }
408 
414 size_t StandAloneClient::readLine(string & msg)
415 {
416  size_t len = 0;
417  char *buf = (char *) nullptr;
418  buf = ::readline("BESClient> ");
419  if (buf && *buf) {
420  len = strlen(buf);
421 #ifdef HAVE_READLINE_HISTORY
422  add_history(buf);
423 #endif
424  if (len > SIZE_COMMUNICATION_BUFFER) {
425  cerr << __FILE__ << __LINE__ <<
426  ": incoming data buffer exceeds maximum capacity with lenght " << len << endl;
427  exit(1);
428  }
429  else {
430  msg = buf;
431  }
432  }
433  else {
434  if (!buf) {
435  // If a null buffer is returned then this means that EOF is
436  // returned. This is different from the user just hitting enter,
437  // which means a character buffer is returned, but is empty.
438 
439  // Problem: len is unsigned.
440  len = -1;
441  }
442  }
443  if (buf) {
444  free(buf);
445  buf = (char *) nullptr;
446  }
447  return len;
448 }
449 
452 void StandAloneClient::displayHelp()
453 {
454  cout << endl;
455  cout << endl;
456  cout << "BES Command Line Client Help" << endl;
457  cout << endl;
458  cout << "Client commands available:" << endl;
459  cout << " exit - exit the command line interface" << endl;
460  cout << " help - display this help screen" << endl;
461  cout << " client suppress; - suppress output from the server" << endl;
462  cout << " client output to screen; - display server output to the screen" << endl;
463  cout << " client output to <file>; - display server output to specified file" << endl;
464  cout << endl;
465  cout << "Any commands beginning with 'client' must end with a semicolon" << endl;
466  cout << endl;
467  cout << "To display the list of commands available from the server " << "please type the command 'show help;'"
468  << endl;
469  cout << endl;
470  cout << endl;
471 }
472 
479 void StandAloneClient::dump(ostream & strm) const
480 {
481  strm << BESIndent::LMarg << "StandAloneClient::dump - (" << (void *) this << ")" << endl;
482  BESIndent::Indent();
483  strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl;
484  strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl;
485  BESIndent::UnIndent();
486 }
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:168
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
virtual int execute_request(const std::string &from)
The entry point for command execution; called by BESServerHandler::execute()
virtual bool start(std::string name)
Definition: BESStopWatch.cc:67
Entry point into BES using xml document requests.
void executeCommands(const std::string &cmd_list, int repeat)
Send the command(s) specified to the BES server after wrapping in request document.
void interact()
An interactive BES client that takes BES requests on the command line.
void executeClientCommand(const std::string &cmd)
Executes a client side command.
virtual void dump(std::ostream &strm) const
dumps information about this object
void setOutput(std::ostream *strm, bool created)
Set the output stream for responses from the BES server.