CmdClient.cc

Go to the documentation of this file.
00001 // CmdClient.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025 
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include "config.h"
00034 
00035 #include <cstdlib>
00036 #include <iostream>
00037 #include <fstream>
00038 #include <sstream>
00039 #include <map>
00040 
00041 using std::cout ;
00042 using std::endl ;
00043 using std::cerr ;
00044 using std::ofstream ;
00045 using std::ostringstream ;
00046 using std::ios ;
00047 using std::map ;
00048 
00049 #ifdef HAVE_LIBREADLINE
00050 #  if defined(HAVE_READLINE_READLINE_H)
00051 #    include <readline/readline.h>
00052 #  elif defined(HAVE_READLINE_H)
00053 #    include <readline.h>
00054 #  else                         /* !defined(HAVE_READLINE_H) */
00055 extern "C"
00056 {
00057     char *readline( const char * ) ;
00058 }
00059 #  endif                        /* !defined(HAVE_READLINE_H) */
00060 char *cmdline = NULL ;
00061 #else                           /* !defined(HAVE_READLINE_READLINE_H) */
00062   /* no readline */
00063 #endif                          /* HAVE_LIBREADLINE */
00064 
00065 #ifdef HAVE_READLINE_HISTORY
00066 #  if defined(HAVE_READLINE_HISTORY_H)
00067 #    include <readline/history.h>
00068 #  elif defined(HAVE_HISTORY_H)
00069 #    include <history.h>
00070 #  else                         /* !defined(HAVE_HISTORY_H) */
00071 extern "C"
00072 {
00073     int add_history( const char * ) ;
00074     int write_history( const char * ) ;
00075     int read_history( const char * ) ;
00076 }
00077 #  endif                        /* defined(HAVE_READLINE_HISTORY_H) */
00078   /* no history */
00079 #endif                          /* HAVE_READLINE_HISTORY */
00080 
00081 #include <libxml/encoding.h>
00082 
00083 #define SIZE_COMMUNICATION_BUFFER 4096*4096
00084 #include "CmdClient.h"
00085 #include "CmdTranslation.h"
00086 #include "PPTClient.h"
00087 #include "BESDebug.h"
00088 #include "BESStopWatch.h"
00089 #include "BESError.h"
00090 
00091 CmdClient::~CmdClient()
00092 {
00093     if( _strmCreated && _strm )
00094     {
00095         _strm->flush() ;
00096         delete _strm ;
00097         _strm = 0 ;
00098     }
00099     else if( _strm )
00100     {
00101         _strm->flush( ) ;
00102     }
00103     if( _client )
00104     {
00105         delete _client ;
00106         _client = 0 ;
00107     }
00108 }
00109 
00124 void
00125 CmdClient::startClient( const string & host, int portVal, int timeout )
00126 {
00127     _client = new PPTClient( host, portVal, timeout ) ;
00128     _client->initConnection() ;
00129 }
00130 
00140 void
00141 CmdClient::startClient( const string & unixStr, int timeout )
00142 {
00143     _client = new PPTClient( unixStr, timeout ) ;
00144     _client->initConnection() ;
00145 }
00146 
00155 void
00156 CmdClient::shutdownClient()
00157 {
00158     if( _client )
00159         _client->closeConnection() ;
00160 }
00161 
00178 void
00179 CmdClient::setOutput( ostream * strm, bool created )
00180 {
00181     if( _strmCreated && _strm )
00182     {
00183         _strm->flush() ;
00184         delete _strm ;
00185     }
00186     else if( _strm )
00187     {
00188         _strm->flush() ;
00189     }
00190     _strm = strm ;
00191     _strmCreated = created ;
00192 }
00193 
00205 bool
00206 CmdClient::executeClientCommand( const string & cmd )
00207 {
00208     bool do_exit = false ;
00209     string suppress = "suppress" ;
00210     if( cmd.compare( 0, suppress.length(), suppress ) == 0 )
00211     {
00212         setOutput( NULL, false ) ;
00213         return do_exit ;
00214     }
00215 
00216     string output = "output to" ;
00217     if( cmd.compare( 0, output.length(), output ) == 0 )
00218     {
00219         string subcmd = cmd.substr( output.length() + 1 ) ;
00220         string screen = "screen" ;
00221         if( subcmd.compare( 0, screen.length(), screen ) == 0 )
00222         {
00223             setOutput( &cout, false ) ;
00224         }
00225         else
00226         {
00227             // subcmd is the name of the file - then semicolon
00228             string file = subcmd.substr( 0, subcmd.length() - 1 ) ;
00229             ofstream *fstrm = new ofstream( file.c_str(), ios::app ) ;
00230             if( fstrm && !(*fstrm) )
00231             {
00232                         delete fstrm ;
00233                 cerr << "Unable to set client output to file " << file
00234                      << endl ;
00235             }
00236             else
00237             {
00238                 setOutput( fstrm, true ) ;
00239             }
00240         }
00241         return do_exit ;
00242     }
00243 
00244     // load commands from an input file and run them
00245     string load = "load" ;
00246     if( cmd.compare( 0, load.length(), load ) == 0 )
00247     {
00248         string file = cmd.substr( load.length() + 1,
00249                                   cmd.length() - load.length() - 2 ) ;
00250         ifstream fstrm( file.c_str() ) ;
00251         if( !fstrm )
00252         {
00253             cerr << "Unable to load commands from file " << file
00254                  << ": file does not exist or failed to open file" << endl ;
00255         }
00256         else
00257         {
00258             do_exit = executeCommands( fstrm, 1 ) ;
00259         }
00260 
00261         return do_exit ;
00262     }
00263 
00264     cerr << "Improper client command " << cmd << endl ;
00265 
00266     return do_exit ;
00267 }
00268 
00281 bool
00282 CmdClient::executeCommand( const string &cmd, int repeat )
00283 {
00284     bool do_exit = false ;
00285     string client = "client" ;
00286     if( cmd.compare( 0, client.length(), client ) == 0 )
00287     {
00288         do_exit = executeClientCommand( cmd.substr( client.length() + 1 ) ) ;
00289     }
00290     else
00291     {
00292         if( repeat < 1 ) repeat = 1 ;
00293         for( int i = 0; i < repeat && !do_exit; i++ )
00294         {
00295             BESDEBUG( "cmdln", "cmdclient sending " << cmd << endl ) ;
00296             BESStopWatch *sw = 0 ;
00297             if( BESISDEBUG( "timing" ) )
00298             {
00299                 sw = new BESStopWatch() ;
00300                 sw->start() ;
00301             }
00302 
00303             map<string,string> extensions ;
00304             _client->send( cmd, extensions ) ;
00305 
00306             BESDEBUG( "cmdln", "cmdclient receiving " << endl ) ;
00307             // keep reading till we get the last chunk, send to _strm
00308             bool done = false ;
00309             ostringstream *show_stream = 0 ;
00310             while( !done )
00311             {
00312                 if( CmdTranslation::is_show() )
00313                 {
00314                     if( !show_stream )
00315                     {
00316                         show_stream = new ostringstream ;
00317                     }
00318                 }
00319                 if( show_stream )
00320                 {
00321                     done = _client->receive( extensions, show_stream ) ;
00322                 }
00323                 else
00324                 {
00325                     done = _client->receive( extensions, _strm ) ;
00326                 }
00327                 if( extensions["status"] == "error" )
00328                 {
00329                     // If there is an error, just flush what I have
00330                     // and continue on.
00331                     _strm->flush() ;
00332 
00333                    // let's also set show to true because we've gotten back
00334                    // an xml document (maybe)
00335                    if( _isInteractive )
00336                    {
00337                         CmdTranslation::set_show( true ) ;
00338                    }
00339                 }
00340                 if( extensions["exit"] == "true" )
00341                 {
00342                     do_exit = true ;
00343                 }
00344             }
00345             if( show_stream )
00346             {
00347                 *(_strm) << show_stream->str() << endl ;
00348                 delete show_stream ;
00349                 show_stream = 0 ;
00350             }
00351             if( BESDebug::IsSet( "cmdln" ) )
00352             {
00353                 BESDEBUG( "cmdln", "extensions:" << endl ) ;
00354                 map<string,string>::const_iterator i = extensions.begin() ;
00355                 map<string,string>::const_iterator e = extensions.end() ;
00356                 for( ; i != e; i++ )
00357                 {
00358                     BESDEBUG( "cmdln", "  " << (*i).first << " = "
00359                                        << (*i).second << endl ) ;
00360                 }
00361                 BESDEBUG( "cmdln", "cmdclient done receiving " << endl ) ;
00362             }
00363             if( BESISDEBUG( "timing" ) )
00364             {
00365                 if( sw && sw->stop() )
00366                 {
00367                     BESDEBUG( "timing", "cmdclient - executed \""
00368                                         << cmd << "\" in " << sw->seconds()
00369                                         << " seconds and " << sw->microseconds()
00370                                         << " microseconds" << endl ) ;
00371                 }
00372                 else
00373                 {
00374                     BESDEBUG( "timing", "cmdclient - executed \"" << cmd
00375                                         << "\" - no timing available"
00376                                         << endl ) ;
00377                 }
00378             }
00379 
00380             _strm->flush() ;
00381             delete sw ;
00382             sw = 0 ;
00383         }
00384     }
00385     return do_exit ;
00386 }
00387 
00405 bool
00406 CmdClient::executeCommands( const string &cmd_list, int repeat )
00407 {
00408     bool do_exit = false ;
00409     _isInteractive = true ;
00410     if( repeat < 1 ) repeat = 1 ;
00411 
00412     CmdTranslation::set_show( false ) ;
00413     try
00414     {
00415         string doc = CmdTranslation::translate( cmd_list ) ;
00416         if( !doc.empty() )
00417         {
00418             do_exit = this->executeCommand( doc, repeat ) ;
00419         }
00420     }
00421     catch( BESError &e )
00422     {
00423         CmdTranslation::set_show( false ) ;
00424         _isInteractive = false ;
00425         throw e ;
00426     }
00427     CmdTranslation::set_show( false ) ;
00428     _isInteractive = false ;
00429     return do_exit ;
00430 }
00431 
00450 bool
00451 CmdClient::executeCommands( ifstream & istrm, int repeat )
00452 {
00453     bool do_exit = false ;
00454     _isInteractive = false ;
00455     if( repeat < 1 ) repeat = 1 ;
00456     for( int i = 0; i < repeat; i++ )
00457     {
00458         istrm.clear( ) ;
00459         istrm.seekg( 0, ios::beg ) ;
00460         string cmd ;
00461         while( !istrm.eof() )
00462         {
00463             char line[4096] ;
00464             line[0] = '\0' ;
00465             istrm.getline( line, 4096, '\n' ) ;
00466             cmd += line ;
00467         }
00468         do_exit = this->executeCommand( cmd, 1 ) ;
00469     }
00470     return do_exit ;
00471 }
00472 
00492 bool
00493 CmdClient::interact()
00494 {
00495     bool do_exit = false ;
00496     _isInteractive = true ;
00497 
00498     cout << endl << endl
00499         << "Type 'exit' to exit the command line client and 'help' or '?' "
00500         << "to display the help screen" << endl << endl ;
00501 
00502     bool done = false ;
00503     while( !done && !do_exit )
00504     {
00505         string message = "" ;
00506         size_t len = this->readLine( message ) ;
00507         if( len == -1 || message == "exit" || message == "exit;" )
00508         {
00509             done = true ;
00510         }
00511         else if( message == "help" || message == "help;" || message == "?" )
00512         {
00513             this->displayHelp() ;
00514         }
00515         else if( message.length() > 6 && message.substr( 0, 6 ) == "client" )
00516         {
00517             do_exit = this->executeCommand( message, 1 ) ;
00518         }
00519         else if( len != 0 && message != "" )
00520         {
00521             CmdTranslation::set_show( false ) ;
00522             try
00523             {
00524                 string doc = CmdTranslation::translate( message ) ;
00525                 if( !doc.empty() )
00526                 {
00527                     do_exit = this->executeCommand( doc, 1 ) ;
00528                 }
00529             }
00530             catch( BESError &e )
00531             {
00532                 CmdTranslation::set_show( false ) ;
00533                 _isInteractive = false ;
00534                 throw e ;
00535             }
00536             CmdTranslation::set_show( false ) ;
00537         }
00538     }
00539     _isInteractive = false ;
00540 
00541     return do_exit ;
00542 }
00543 
00549 size_t
00550 CmdClient::readLine( string &msg )
00551 {
00552     size_t len = 0 ;
00553     char *buf = (char *) NULL ;
00554     buf =::readline( "BESClient> " ) ;
00555     if( buf && *buf )
00556     {
00557         len = strlen( buf ) ;
00558 #ifdef HAVE_READLINE_HISTORY
00559         add_history( buf ) ;
00560 #endif
00561         if( len > SIZE_COMMUNICATION_BUFFER )
00562         {
00563             cerr << __FILE__ << __LINE__
00564                 <<
00565                 ": incoming data buffer exceeds maximum capacity with lenght "
00566                 << len << endl ;
00567             exit( 1 ) ;
00568         }
00569         else {
00570             msg = buf ;
00571         }
00572     }
00573     else {
00574         if( !buf )
00575         {
00576             // If a null buffer is returned then this means that EOF is
00577             // returned. This is different from the user just hitting enter,
00578             // which means a character buffer is returned, but is empty.
00579 
00580             // Problem: len is unsigned.
00581             len = -1 ;
00582         }
00583     }
00584     if( buf )
00585     {
00586         free( buf ) ;
00587         buf = (char *)NULL ;
00588     }
00589     return len ;
00590 }
00591 
00594 void
00595 CmdClient::displayHelp()
00596 {
00597     cout << endl ;
00598     cout << endl ;
00599     cout << "BES Command Line Client Help" << endl ;
00600     cout << endl ;
00601     cout << "Client commands available:" << endl ;
00602     cout <<
00603         "    exit                     - exit the command line interface" <<
00604         endl ;
00605     cout << "    help                     - display this help screen" <<
00606         endl ;
00607     cout <<
00608         "    client suppress;         - suppress output from the server" <<
00609         endl ;
00610     cout <<
00611         "    client output to screen; - display server output to the screen"
00612         << endl ;
00613     cout <<
00614         "    client output to <file>; - display server output to specified file"
00615         << endl ;
00616     cout <<
00617         "    client load <file>; - load xml document from file"
00618         << endl ;
00619     cout << endl ;
00620     cout <<
00621         "Any commands beginning with 'client' must end with a semicolon" <<
00622         endl ;
00623     cout << endl ;
00624     cout << "To display the list of commands available from the server "
00625         << "please type the command 'show help;'" << endl ;
00626     cout << endl ;
00627     cout << endl ;
00628 }
00629 
00634 bool
00635 CmdClient::isConnected()
00636 {
00637     if( _client )
00638         return _client->isConnected() ;
00639     return false ;
00640 }
00641 
00644 void
00645 CmdClient::brokenPipe()
00646 {
00647     if( _client )
00648         _client->brokenPipe() ;
00649 }
00650 
00657 void
00658 CmdClient::dump( ostream & strm ) const
00659 {
00660     strm << BESIndent::LMarg << "CmdClient::dump - ("
00661         << (void *) this << ")" << endl ;
00662     BESIndent::Indent() ;
00663     if( _client )
00664     {
00665         strm << BESIndent::LMarg << "client:" << endl ;
00666         BESIndent::Indent() ;
00667         _client->dump( strm ) ;
00668         BESIndent::UnIndent() ;
00669     }
00670     else
00671     {
00672         strm << BESIndent::LMarg << "client: null" << endl ;
00673     }
00674     strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl ;
00675     strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl ;
00676     BESIndent::UnIndent() ;
00677 }

Generated on Thu Sep 16 15:20:31 2010 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.4.7