OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
CmdClient.cc
Go to the documentation of this file.
1 // CmdClient.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 "config.h"
34 
35 #include <cstdlib>
36 #include <iostream>
37 #include <fstream>
38 #include <sstream>
39 #include <map>
40 
41 using std::cout;
42 using std::endl;
43 using std::cerr;
44 using std::ofstream;
45 using std::ostringstream;
46 using std::ios;
47 using std::map;
48 
49 #ifdef HAVE_LIBREADLINE
50 # if defined(HAVE_READLINE_READLINE_H)
51 # include <readline/readline.h>
52 # elif defined(HAVE_READLINE_H)
53 # include <readline.h>
54 # else /* !defined(HAVE_READLINE_H) */
55 extern "C"
56 {
57  char *readline( const char * );
58 }
59 # endif /* !defined(HAVE_READLINE_H) */
60 char *cmdline = NULL;
61 #else /* !defined(HAVE_READLINE_READLINE_H) */
62 /* no readline */
63 #endif /* HAVE_LIBREADLINE */
64 
65 #ifdef HAVE_READLINE_HISTORY
66 # if defined(HAVE_READLINE_HISTORY_H)
67 # include <readline/history.h>
68 # elif defined(HAVE_HISTORY_H)
69 # include <history.h>
70 # else /* !defined(HAVE_HISTORY_H) */
71 extern "C"
72 {
73  int add_history( const char * );
74  int write_history( const char * );
75  int read_history( const char * );
76 }
77 # endif /* defined(HAVE_READLINE_HISTORY_H) */
78 /* no history */
79 #endif /* HAVE_READLINE_HISTORY */
80 
81 #include <libxml/encoding.h>
82 
83 #define SIZE_COMMUNICATION_BUFFER 4096*4096
84 #include "CmdClient.h"
85 #include "CmdTranslation.h"
86 #include "PPTClient.h"
87 #include "BESDebug.h"
88 #include "BESStopWatch.h"
89 #include "BESError.h"
90 
92 {
93  if (_strmCreated && _strm) {
94  _strm->flush();
95  delete _strm;
96  _strm = 0;
97  }
98  else if (_strm) {
99  _strm->flush();
100  }
101  if (_client) {
102  delete _client;
103  _client = 0;
104  }
105 }
106 
121 void CmdClient::startClient(const string & host, int portVal, int timeout)
122 {
123  _client = new PPTClient(host, portVal, timeout);
124  _client->initConnection();
125 }
126 
136 void CmdClient::startClient(const string & unixStr, int timeout)
137 {
138  _client = new PPTClient(unixStr, timeout);
139  _client->initConnection();
140 }
141 
151 {
152  if (_client) _client->closeConnection();
153 }
154 
171 void CmdClient::setOutput(ostream * strm, bool created)
172 {
173  if (_strmCreated && _strm) {
174  _strm->flush();
175  delete _strm;
176  }
177  else if (_strm) {
178  _strm->flush();
179  }
180  _strm = strm;
181  _strmCreated = created;
182 }
183 
195 bool CmdClient::executeClientCommand(const string & cmd)
196 {
197  bool do_exit = false;
198  string suppress = "suppress";
199  if (cmd.compare(0, suppress.length(), suppress) == 0) {
200  setOutput(NULL, false);
201  return do_exit;
202  }
203 
204  string output = "output to";
205  if (cmd.compare(0, output.length(), output) == 0) {
206  string subcmd = cmd.substr(output.length() + 1);
207  string screen = "screen";
208  if (subcmd.compare(0, screen.length(), screen) == 0) {
209  setOutput(&cout, false);
210  }
211  else {
212  // subcmd is the name of the file - then semicolon
213  string file = subcmd.substr(0, subcmd.length() - 1);
214  ofstream *fstrm = new ofstream(file.c_str(), ios::app);
215  if (fstrm && !(*fstrm)) {
216  delete fstrm;
217  cerr << "Unable to set client output to file " << file << endl;
218  }
219  else {
220  setOutput(fstrm, true);
221  }
222  }
223  return do_exit;
224  }
225 
226  // load commands from an input file and run them
227  string load = "load";
228  if (cmd.compare(0, load.length(), load) == 0) {
229  string file = cmd.substr(load.length() + 1, cmd.length() - load.length() - 2);
230  ifstream fstrm(file.c_str());
231  if (!fstrm) {
232  cerr << "Unable to load commands from file " << file << ": file does not exist or failed to open file"
233  << endl;
234  }
235  else {
236  do_exit = executeCommands(fstrm, 1);
237  }
238 
239  return do_exit;
240  }
241 
242  cerr << "Improper client command " << cmd << endl;
243 
244  return do_exit;
245 }
246 
259 bool CmdClient::executeCommand(const string &cmd, int repeat)
260 {
261  bool do_exit = false;
262  const string client = "client";
263  if (cmd.compare(0, client.length(), client) == 0) {
264  do_exit = executeClientCommand(cmd.substr(client.length() + 1));
265  }
266  else {
267  if (repeat < 1) repeat = 1;
268  for (int i = 0; i < repeat && !do_exit; i++) {
269  BESDEBUG( "cmdln", "cmdclient sending " << cmd << endl );
270  BESStopWatch *sw = 0;
271  if( BESISDEBUG( "timing" ) )
272  {
273  sw = new BESStopWatch();
274  sw->start();
275  }
276 
277  map<string,string> extensions;
278  _client->send( cmd, extensions );
279 
280  BESDEBUG( "cmdln", "cmdclient receiving " << endl );
281  // keep reading till we get the last chunk, send to _strm
282  bool done = false;
283  ostringstream *show_stream = 0;
284  while( !done )
285  {
287  {
288  if( !show_stream )
289  {
290  show_stream = new ostringstream;
291  }
292  }
293  if( show_stream )
294  {
295  done = _client->receive( extensions, show_stream );
296  }
297  else
298  {
299  done = _client->receive( extensions, _strm );
300  }
301  if( extensions["status"] == "error" )
302  {
303  // If there is an error, just flush what I have
304  // and continue on.
305  _strm->flush();
306 
307  // let's also set show to true because we've gotten back
308  // an xml document (maybe)
309  if( _isInteractive )
310  {
311  CmdTranslation::set_show( true );
312  }
313  }
314  if( extensions["exit"] == "true" )
315  {
316  do_exit = true;
317  }
318  }
319  if( show_stream )
320  {
321  *(_strm) << show_stream->str() << endl;
322  delete show_stream;
323  show_stream = 0;
324  }
325  if( BESDebug::IsSet( "cmdln" ) )
326  {
327  BESDEBUG( "cmdln", "extensions:" << endl );
328  map<string,string>::const_iterator i = extensions.begin();
329  map<string,string>::const_iterator e = extensions.end();
330  for(; i != e; i++ )
331  {
332  BESDEBUG( "cmdln", " " << (*i).first << " = "
333  << (*i).second << endl );
334  }
335  BESDEBUG( "cmdln", "cmdclient done receiving " << endl );
336  }
337  if( BESISDEBUG( "timing" ) )
338  {
339  if( sw && sw->stop() )
340  {
341  BESDEBUG( "timing", "cmdclient - executed \""
342  << cmd << "\" in " << sw->seconds()
343  << " seconds and " << sw->microseconds()
344  << " microseconds" << endl );
345  }
346  else
347  {
348  BESDEBUG( "timing", "cmdclient - executed \"" << cmd
349  << "\" - no timing available"
350  << endl );
351  }
352  }
353 
354  _strm->flush();
355  delete sw;
356  sw = 0;
357  }
358  }
359  return do_exit;
360 }
361 
379 bool CmdClient::executeCommands(const string &cmd_list, int repeat)
380 {
381  bool do_exit = false;
382  _isInteractive = true;
383  if (repeat < 1) repeat = 1;
384 
386  try {
387  string doc = CmdTranslation::translate(cmd_list);
388  if (!doc.empty()) {
389  do_exit = this->executeCommand(doc, repeat);
390  }
391  }
392  catch (BESError &e) {
394  _isInteractive = false;
395  throw e;
396  }
398  _isInteractive = false;
399  return do_exit;
400 }
401 
420 bool CmdClient::executeCommands(ifstream & istrm, int repeat)
421 {
422  bool do_exit = false;
423  _isInteractive = false;
424  if (repeat < 1) repeat = 1;
425  for (int i = 0; i < repeat; i++) {
426  istrm.clear();
427  istrm.seekg(0, ios::beg);
428  string cmd;
429  while (!istrm.eof()) {
430  char line[4096];
431  line[0] = '\0';
432  istrm.getline(line, 4096, '\n');
433  cmd += line;
434  }
435  do_exit = this->executeCommand(cmd, 1);
436  }
437  return do_exit;
438 }
439 
460 {
461  bool do_exit = false;
462  _isInteractive = true;
463 
464  cout << endl << endl << "Type 'exit' to exit the command line client and 'help' or '?' "
465  << "to display the help screen" << endl << endl;
466 
467  bool done = false;
468  while (!done && !do_exit) {
469  string message = "";
470  size_t len = this->readLine(message);
471  // len is unsigned. jhrg 11/5/13
472  if (/* len == -1 || */ message == "exit" || message == "exit;") {
473  done = true;
474  }
475  else if (message == "help" || message == "help;" || message == "?") {
476  this->displayHelp();
477  }
478  else if (message.length() > 6 && message.substr(0, 6) == "client") {
479  do_exit = this->executeCommand(message, 1);
480  }
481  else if (len != 0 && message != "") {
483  try {
484  string doc = CmdTranslation::translate(message);
485  if (!doc.empty()) {
486  do_exit = this->executeCommand(doc, 1);
487  }
488  }
489  catch (BESError &e) {
491  _isInteractive = false;
492  throw e;
493  }
495  }
496  }
497  _isInteractive = false;
498 
499  return do_exit;
500 }
501 
509 size_t CmdClient::readLine(string &msg)
510 {
511  size_t len = 0;
512  char *buf = (char *) NULL;
513  buf = ::readline("BESClient> ");
514  if (buf && *buf) {
515  len = strlen(buf);
516 #ifdef HAVE_READLINE_HISTORY
517  add_history(buf);
518 #endif
519  if (len > SIZE_COMMUNICATION_BUFFER) {
520  cerr << __FILE__ << __LINE__ <<
521  ": incoming data buffer exceeds maximum capacity with lenght " << len << endl;
522  exit(1);
523  }
524  else {
525  msg = buf;
526  }
527  }
528  else {
529  if (!buf) {
530  // If a null buffer is returned then this means that EOF is
531  // returned. This is different from the user just hitting enter,
532  // which means a character buffer is returned, but is empty.
533 
534  // Problem: len is unsigned.
535  // len = -1 ; I replaced this with the following. jhrg 1/4/12
536  len = 0;
537  }
538  }
539  if (buf) {
540  free(buf);
541  buf = (char *) NULL;
542  }
543  return len;
544 }
545 
548 void CmdClient::displayHelp()
549 {
550  cout << endl;
551  cout << endl;
552  cout << "BES Command Line Client Help" << endl;
553  cout << endl;
554  cout << "Client commands available:" << endl;
555  cout << " exit - exit the command line interface" << endl;
556  cout << " help - display this help screen" << endl;
557  cout << " client suppress; - suppress output from the server" << endl;
558  cout << " client output to screen; - display server output to the screen" << endl;
559  cout << " client output to <file>; - display server output to specified file" << endl;
560  cout << " client load <file>; - load xml document from file" << endl;
561  cout << endl;
562  cout << "Any commands beginning with 'client' must end with a semicolon" << endl;
563  cout << endl;
564  cout << "To display the list of commands available from the server " << "please type the command 'show help;'"
565  << endl;
566  cout << endl;
567  cout << endl;
568 }
569 
575 {
576  if (_client) return _client->isConnected();
577  return false;
578 }
579 
583 {
584  if (_client) _client->brokenPipe();
585 }
586 
593 void CmdClient::dump(ostream & strm) const
594 {
595  strm << BESIndent::LMarg << "CmdClient::dump - (" << (void *) this << ")" << endl;
597  if (_client) {
598  strm << BESIndent::LMarg << "client:" << endl;
600  _client->dump(strm);
602  }
603  else {
604  strm << BESIndent::LMarg << "client: null" << endl;
605  }
606  strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl;
607  strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl;
609 }
virtual int seconds()
Definition: BESStopWatch.h:58
void shutdownClient()
Closes the connection to the OpeNDAP server and closes the output stream.
Definition: CmdClient.cc:150
#define BESISDEBUG(x)
macro used to determine if the specified debug context is set
Definition: BESDebug.h:83
virtual bool receive(map< string, string > &extensions, ostream *strm=0)
receive a chunk of either extensions into the specified map or data into the specified stream ...
static bool is_show()
bool executeClientCommand(const string &cmd)
Executes a client side command.
Definition: CmdClient.cc:195
virtual int microseconds()
Definition: BESStopWatch.h:63
virtual void closeConnection()
Definition: PPTClient.cc:220
virtual bool isConnected()
Definition: Connection.h:81
void brokenPipe()
inform the server that there has been a borken pipe
Definition: CmdClient.cc:582
static void set_show(bool val)
virtual bool stop()
Definition: BESStopWatch.cc:77
static void Indent()
Definition: BESIndent.cc:38
static string translate(const string &commands)
virtual void initConnection()
Definition: PPTClient.cc:120
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
virtual void brokenPipe()
Definition: Connection.h:96
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
bool isConnected()
return whether the client is connected to the BES
Definition: CmdClient.cc:574
static bool IsSet(const string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:160
~CmdClient()
Definition: CmdClient.cc:91
virtual void dump(ostream &strm) const
dumps information about this object
Definition: CmdClient.cc:593
void startClient(const string &host, int portVal, int timeout)
Connect the BES client to the BES server.
Definition: CmdClient.cc:121
virtual bool start()
Definition: BESStopWatch.cc:45
void setOutput(ostream *strm, bool created)
Set the output stream for responses from the BES server.
Definition: CmdClient.cc:171
virtual void dump(ostream &strm) const
dumps information about this object
Definition: PPTClient.cc:252
bool executeCommands(const string &cmd, int repeat)
Send the command(s) specified to the BES server after wrapping in request document.
Definition: CmdClient.cc:379
bool interact()
An interactive BES client that takes BES requests on the command line.
Definition: CmdClient.cc:459
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
virtual void send(const string &buffer)
sends the buffer to the socket
#define SIZE_COMMUNICATION_BUFFER
Definition: CmdClient.cc:83