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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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  {
95  _strm->flush() ;
96  delete _strm ;
97  _strm = 0 ;
98  }
99  else if( _strm )
100  {
101  _strm->flush( ) ;
102  }
103  if( _client )
104  {
105  delete _client ;
106  _client = 0 ;
107  }
108 }
109 
124 void
125 CmdClient::startClient( const string & host, int portVal, int timeout )
126 {
127  _client = new PPTClient( host, portVal, timeout ) ;
128  _client->initConnection() ;
129 }
130 
140 void
141 CmdClient::startClient( const string & unixStr, int timeout )
142 {
143  _client = new PPTClient( unixStr, timeout ) ;
144  _client->initConnection() ;
145 }
146 
155 void
157 {
158  if( _client )
159  _client->closeConnection() ;
160 }
161 
178 void
179 CmdClient::setOutput( ostream * strm, bool created )
180 {
181  if( _strmCreated && _strm )
182  {
183  _strm->flush() ;
184  delete _strm ;
185  }
186  else if( _strm )
187  {
188  _strm->flush() ;
189  }
190  _strm = strm ;
191  _strmCreated = created ;
192 }
193 
205 bool
206 CmdClient::executeClientCommand( const string & cmd )
207 {
208  bool do_exit = false ;
209  string suppress = "suppress" ;
210  if( cmd.compare( 0, suppress.length(), suppress ) == 0 )
211  {
212  setOutput( NULL, false ) ;
213  return do_exit ;
214  }
215 
216  string output = "output to" ;
217  if( cmd.compare( 0, output.length(), output ) == 0 )
218  {
219  string subcmd = cmd.substr( output.length() + 1 ) ;
220  string screen = "screen" ;
221  if( subcmd.compare( 0, screen.length(), screen ) == 0 )
222  {
223  setOutput( &cout, false ) ;
224  }
225  else
226  {
227  // subcmd is the name of the file - then semicolon
228  string file = subcmd.substr( 0, subcmd.length() - 1 ) ;
229  ofstream *fstrm = new ofstream( file.c_str(), ios::app ) ;
230  if( fstrm && !(*fstrm) )
231  {
232  delete fstrm ;
233  cerr << "Unable to set client output to file " << file
234  << endl ;
235  }
236  else
237  {
238  setOutput( fstrm, true ) ;
239  }
240  }
241  return do_exit ;
242  }
243 
244  // load commands from an input file and run them
245  string load = "load" ;
246  if( cmd.compare( 0, load.length(), load ) == 0 )
247  {
248  string file = cmd.substr( load.length() + 1,
249  cmd.length() - load.length() - 2 ) ;
250  ifstream fstrm( file.c_str() ) ;
251  if( !fstrm )
252  {
253  cerr << "Unable to load commands from file " << file
254  << ": file does not exist or failed to open file" << endl ;
255  }
256  else
257  {
258  do_exit = executeCommands( fstrm, 1 ) ;
259  }
260 
261  return do_exit ;
262  }
263 
264  cerr << "Improper client command " << cmd << endl ;
265 
266  return do_exit ;
267 }
268 
281 bool
282 CmdClient::executeCommand( const string &cmd, int repeat )
283 {
284  bool do_exit = false ;
285  string client = "client" ;
286  if( cmd.compare( 0, client.length(), client ) == 0 )
287  {
288  do_exit = executeClientCommand( cmd.substr( client.length() + 1 ) ) ;
289  }
290  else
291  {
292  if( repeat < 1 ) repeat = 1 ;
293  for( int i = 0; i < repeat && !do_exit; i++ )
294  {
295  BESDEBUG( "cmdln", "cmdclient sending " << cmd << endl ) ;
296  BESStopWatch *sw = 0 ;
297  if( BESISDEBUG( "timing" ) )
298  {
299  sw = new BESStopWatch() ;
300  sw->start() ;
301  }
302 
303  map<string,string> extensions ;
304  _client->send( cmd, extensions ) ;
305 
306  BESDEBUG( "cmdln", "cmdclient receiving " << endl ) ;
307  // keep reading till we get the last chunk, send to _strm
308  bool done = false ;
309  ostringstream *show_stream = 0 ;
310  while( !done )
311  {
313  {
314  if( !show_stream )
315  {
316  show_stream = new ostringstream ;
317  }
318  }
319  if( show_stream )
320  {
321  done = _client->receive( extensions, show_stream ) ;
322  }
323  else
324  {
325  done = _client->receive( extensions, _strm ) ;
326  }
327  if( extensions["status"] == "error" )
328  {
329  // If there is an error, just flush what I have
330  // and continue on.
331  _strm->flush() ;
332 
333  // let's also set show to true because we've gotten back
334  // an xml document (maybe)
335  if( _isInteractive )
336  {
337  CmdTranslation::set_show( true ) ;
338  }
339  }
340  if( extensions["exit"] == "true" )
341  {
342  do_exit = true ;
343  }
344  }
345  if( show_stream )
346  {
347  *(_strm) << show_stream->str() << endl ;
348  delete show_stream ;
349  show_stream = 0 ;
350  }
351  if( BESDebug::IsSet( "cmdln" ) )
352  {
353  BESDEBUG( "cmdln", "extensions:" << endl ) ;
354  map<string,string>::const_iterator i = extensions.begin() ;
355  map<string,string>::const_iterator e = extensions.end() ;
356  for( ; i != e; i++ )
357  {
358  BESDEBUG( "cmdln", " " << (*i).first << " = "
359  << (*i).second << endl ) ;
360  }
361  BESDEBUG( "cmdln", "cmdclient done receiving " << endl ) ;
362  }
363  if( BESISDEBUG( "timing" ) )
364  {
365  if( sw && sw->stop() )
366  {
367  BESDEBUG( "timing", "cmdclient - executed \""
368  << cmd << "\" in " << sw->seconds()
369  << " seconds and " << sw->microseconds()
370  << " microseconds" << endl ) ;
371  }
372  else
373  {
374  BESDEBUG( "timing", "cmdclient - executed \"" << cmd
375  << "\" - no timing available"
376  << endl ) ;
377  }
378  }
379 
380  _strm->flush() ;
381  delete sw ;
382  sw = 0 ;
383  }
384  }
385  return do_exit ;
386 }
387 
405 bool
406 CmdClient::executeCommands( const string &cmd_list, int repeat )
407 {
408  bool do_exit = false ;
409  _isInteractive = true ;
410  if( repeat < 1 ) repeat = 1 ;
411 
412  CmdTranslation::set_show( false ) ;
413  try
414  {
415  string doc = CmdTranslation::translate( cmd_list ) ;
416  if( !doc.empty() )
417  {
418  do_exit = this->executeCommand( doc, repeat ) ;
419  }
420  }
421  catch( BESError &e )
422  {
423  CmdTranslation::set_show( false ) ;
424  _isInteractive = false ;
425  throw e ;
426  }
427  CmdTranslation::set_show( false ) ;
428  _isInteractive = false ;
429  return do_exit ;
430 }
431 
450 bool
451 CmdClient::executeCommands( ifstream & istrm, int repeat )
452 {
453  bool do_exit = false ;
454  _isInteractive = false ;
455  if( repeat < 1 ) repeat = 1 ;
456  for( int i = 0; i < repeat; i++ )
457  {
458  istrm.clear( ) ;
459  istrm.seekg( 0, ios::beg ) ;
460  string cmd ;
461  while( !istrm.eof() )
462  {
463  char line[4096] ;
464  line[0] = '\0' ;
465  istrm.getline( line, 4096, '\n' ) ;
466  cmd += line ;
467  }
468  do_exit = this->executeCommand( cmd, 1 ) ;
469  }
470  return do_exit ;
471 }
472 
492 bool
494 {
495  bool do_exit = false ;
496  _isInteractive = true ;
497 
498  cout << endl << endl
499  << "Type 'exit' to exit the command line client and 'help' or '?' "
500  << "to display the help screen" << endl << endl ;
501 
502  bool done = false ;
503  while( !done && !do_exit )
504  {
505  string message = "" ;
506  size_t len = this->readLine( message ) ;
507  if( len == -1 || message == "exit" || message == "exit;" )
508  {
509  done = true ;
510  }
511  else if( message == "help" || message == "help;" || message == "?" )
512  {
513  this->displayHelp() ;
514  }
515  else if( message.length() > 6 && message.substr( 0, 6 ) == "client" )
516  {
517  do_exit = this->executeCommand( message, 1 ) ;
518  }
519  else if( len != 0 && message != "" )
520  {
521  CmdTranslation::set_show( false ) ;
522  try
523  {
524  string doc = CmdTranslation::translate( message ) ;
525  if( !doc.empty() )
526  {
527  do_exit = this->executeCommand( doc, 1 ) ;
528  }
529  }
530  catch( BESError &e )
531  {
532  CmdTranslation::set_show( false ) ;
533  _isInteractive = false ;
534  throw e ;
535  }
536  CmdTranslation::set_show( false ) ;
537  }
538  }
539  _isInteractive = false ;
540 
541  return do_exit ;
542 }
543 
551 size_t
552 CmdClient::readLine( string &msg )
553 {
554  size_t len = 0 ;
555  char *buf = (char *) NULL ;
556  buf = ::readline( "BESClient> " ) ;
557  if( buf && *buf )
558  {
559  len = strlen( buf ) ;
560 #ifdef HAVE_READLINE_HISTORY
561  add_history( buf ) ;
562 #endif
563  if( len > SIZE_COMMUNICATION_BUFFER )
564  {
565  cerr << __FILE__ << __LINE__
566  <<
567  ": incoming data buffer exceeds maximum capacity with lenght "
568  << len << endl ;
569  exit( 1 ) ;
570  }
571  else {
572  msg = buf ;
573  }
574  }
575  else {
576  if( !buf )
577  {
578  // If a null buffer is returned then this means that EOF is
579  // returned. This is different from the user just hitting enter,
580  // which means a character buffer is returned, but is empty.
581 
582  // Problem: len is unsigned.
583  // len = -1 ; I replaced this with the following. jhrg 1/4/12
584  len = 0;
585  }
586  }
587  if( buf )
588  {
589  free( buf ) ;
590  buf = (char *)NULL ;
591  }
592  return len ;
593 }
594 
597 void
598 CmdClient::displayHelp()
599 {
600  cout << endl ;
601  cout << endl ;
602  cout << "BES Command Line Client Help" << endl ;
603  cout << endl ;
604  cout << "Client commands available:" << endl ;
605  cout <<
606  " exit - exit the command line interface" <<
607  endl ;
608  cout << " help - display this help screen" <<
609  endl ;
610  cout <<
611  " client suppress; - suppress output from the server" <<
612  endl ;
613  cout <<
614  " client output to screen; - display server output to the screen"
615  << endl ;
616  cout <<
617  " client output to <file>; - display server output to specified file"
618  << endl ;
619  cout <<
620  " client load <file>; - load xml document from file"
621  << endl ;
622  cout << endl ;
623  cout <<
624  "Any commands beginning with 'client' must end with a semicolon" <<
625  endl ;
626  cout << endl ;
627  cout << "To display the list of commands available from the server "
628  << "please type the command 'show help;'" << endl ;
629  cout << endl ;
630  cout << endl ;
631 }
632 
637 bool
639 {
640  if( _client )
641  return _client->isConnected() ;
642  return false ;
643 }
644 
647 void
649 {
650  if( _client )
651  _client->brokenPipe() ;
652 }
653 
660 void
661 CmdClient::dump( ostream & strm ) const
662 {
663  strm << BESIndent::LMarg << "CmdClient::dump - ("
664  << (void *) this << ")" << endl ;
666  if( _client )
667  {
668  strm << BESIndent::LMarg << "client:" << endl ;
670  _client->dump( strm ) ;
672  }
673  else
674  {
675  strm << BESIndent::LMarg << "client: null" << endl ;
676  }
677  strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl ;
678  strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl ;
680 }
virtual int seconds()
Definition: BESStopWatch.h:58
void shutdownClient()
Closes the connection to the OpeNDAP server and closes the output stream.
Definition: CmdClient.cc:156
#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:206
virtual int microseconds()
Definition: BESStopWatch.h:63
virtual void closeConnection()
Definition: PPTClient.cc:220
virtual bool isConnected()
Definition: Connection.h:82
void brokenPipe()
inform the server that there has been a borken pipe
Definition: CmdClient.cc:648
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:98
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
bool isConnected()
return whether the client is connected to the BES
Definition: CmdClient.cc:638
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:661
void startClient(const string &host, int portVal, int timeout)
Connect the BES client to the BES server.
Definition: CmdClient.cc:125
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:179
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:406
bool interact()
An interactive BES client that takes BES requests on the command line.
Definition: CmdClient.cc:493
#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