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 "CmdPretty.h"
00087 #include "PPTClient.h"
00088 #include "BESDebug.h"
00089 #include "BESStopWatch.h"
00090 #include "BESError.h"
00091 
00092 CmdClient::~CmdClient()
00093 {
00094     if( _strmCreated && _strm )
00095     {
00096         _strm->flush() ;
00097         delete _strm ;
00098         _strm = 0 ;
00099     }
00100     else if( _strm )
00101     {
00102         _strm->flush( ) ;
00103     }
00104     if( _client )
00105     {
00106         delete _client ;
00107         _client = 0 ;
00108     }
00109 }
00110 
00125 void
00126 CmdClient::startClient( const string & host, int portVal, int timeout )
00127 {
00128     _client = new PPTClient( host, portVal, timeout ) ;
00129     _client->initConnection() ;
00130 }
00131 
00141 void
00142 CmdClient::startClient( const string & unixStr, int timeout )
00143 {
00144     _client = new PPTClient( unixStr, timeout ) ;
00145     _client->initConnection() ;
00146 }
00147 
00156 void
00157 CmdClient::shutdownClient()
00158 {
00159     if( _client )
00160         _client->closeConnection() ;
00161 }
00162 
00179 void
00180 CmdClient::setOutput( ostream * strm, bool created )
00181 {
00182     if( _strmCreated && _strm )
00183     {
00184         _strm->flush() ;
00185         delete _strm ;
00186     }
00187     else if( _strm )
00188     {
00189         _strm->flush() ;
00190     }
00191     _strm = strm ;
00192     _strmCreated = created ;
00193 }
00194 
00206 void
00207 CmdClient::executeClientCommand( const string & cmd )
00208 {
00209     string suppress = "suppress" ;
00210     if( cmd.compare( 0, suppress.length(), suppress ) == 0 )
00211     {
00212         setOutput( NULL, false ) ;
00213         return ;
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 ;
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             executeCommands( fstrm, 1 ) ;
00259         }
00260 
00261         return ;
00262     }
00263 
00264     cerr << "Improper client command " << cmd << endl ;
00265 }
00266 
00279 void
00280 CmdClient::executeCommand( const string &cmd, int repeat )
00281 {
00282     string client = "client" ;
00283     if( cmd.compare( 0, client.length(), client ) == 0 )
00284     {
00285         executeClientCommand( cmd.substr( client.length() + 1 ) ) ;
00286     }
00287     else
00288     {
00289         if( repeat < 1 ) repeat = 1 ;
00290         for( int i = 0; i < repeat; i++ )
00291         {
00292             BESDEBUG( "cmdln", "cmdclient sending " << cmd << endl )
00293             BESStopWatch *sw = 0 ;
00294             if( BESISDEBUG( "timing" ) )
00295             {
00296                 sw = new BESStopWatch() ;
00297                 sw->start() ;
00298             }
00299 
00300             map<string,string> extensions ;
00301             _client->send( cmd, extensions ) ;
00302 
00303             BESDEBUG( "cmdln", "cmdclient receiving " << endl )
00304             // keep reading till we get the last chunk, send to _strm
00305             bool done = false ;
00306             ostringstream *show_stream = 0 ;
00307             while( !done )
00308             {
00309                 if( CmdTranslation::is_show() )
00310                 {
00311                     if( !show_stream )
00312                     {
00313                         show_stream = new ostringstream ;
00314                     }
00315                 }
00316                 if( show_stream )
00317                 {
00318                     done = _client->receive( extensions, show_stream ) ;
00319                 }
00320                 else
00321                 {
00322                     done = _client->receive( extensions, _strm ) ;
00323                 }
00324                 if( extensions["status"] == "error" )
00325                 {
00326                     // If there is an error, just flush what I have
00327                     // and continue on.
00328                     _strm->flush() ;
00329 
00330                    // let's also set show to true because we've gotten back
00331                    // an xml document (maybe)
00332                    if( _isInteractive )
00333                    {
00334                        CmdTranslation::set_show( true ) ;
00335                    }
00336 
00337                 }
00338             }
00339             if( show_stream )
00340             {
00341                 CmdPretty::make_pretty( show_stream->str(), *_strm ) ;
00342                 delete show_stream ;
00343                 show_stream = 0 ;
00344             }
00345             if( BESDebug::IsSet( "cmdln" ) )
00346             {
00347                 BESDEBUG( "cmdln", "extensions:" << endl )
00348                 map<string,string>::const_iterator i = extensions.begin() ;
00349                 map<string,string>::const_iterator e = extensions.end() ;
00350                 for( ; i != e; i++ )
00351                 {
00352                     BESDEBUG( "cmdln", "  " << (*i).first << " = " << (*i).second << endl )
00353                 }
00354                 BESDEBUG( "cmdln", "cmdclient done receiving " << endl )
00355             }
00356             if( BESISDEBUG( "timing" ) )
00357             {
00358                 if( sw && sw->stop() )
00359                 {
00360                     BESDEBUG( "timing",
00361                               "cmdclient - executed \"" << cmd << "\" in "
00362                               << sw->seconds() << " seconds and "
00363                               << sw->microseconds() << " microseconds"
00364                               << endl )
00365                 }
00366                 else
00367                 {
00368                     BESDEBUG( "timing", \
00369                           "cmdclient - executed \"" << cmd
00370                           << "\" - no timing available" << endl )
00371                 }
00372             }
00373 
00374             _strm->flush() ;
00375                 delete sw ;
00376                 sw = 0 ;
00377         }
00378     }
00379 }
00380 
00398 void
00399 CmdClient::executeCommands( const string &cmd_list, int repeat )
00400 {
00401     _isInteractive = true ;
00402     if( repeat < 1 ) repeat = 1 ;
00403 
00404     CmdTranslation::set_show( false ) ;
00405     try
00406     {
00407         string doc = CmdTranslation::translate( cmd_list ) ;
00408         if( !doc.empty() )
00409         {
00410             this->executeCommand( doc, repeat ) ;
00411         }
00412     }
00413     catch( BESError &e )
00414     {
00415         CmdTranslation::set_show( false ) ;
00416         _isInteractive = false ;
00417         throw e ;
00418     }
00419     CmdTranslation::set_show( false ) ;
00420     _isInteractive = false ;
00421 }
00422 
00441 void
00442 CmdClient::executeCommands( ifstream & istrm, int repeat )
00443 {
00444     _isInteractive = false ;
00445     if( repeat < 1 ) repeat = 1 ;
00446     for( int i = 0; i < repeat; i++ )
00447     {
00448         istrm.clear( ) ;
00449         istrm.seekg( 0, ios::beg ) ;
00450         string cmd ;
00451         while( !istrm.eof() )
00452         {
00453             char line[4096] ;
00454             line[0] = '\0' ;
00455             istrm.getline( line, 4096, '\n' ) ;
00456             cmd += line ;
00457         }
00458         this->executeCommand( cmd, 1 ) ;
00459     }
00460 }
00461 
00481 void
00482 CmdClient::interact()
00483 {
00484     _isInteractive = true ;
00485 
00486     cout << endl << endl
00487         << "Type 'exit' to exit the command line client and 'help' or '?' "
00488         << "to display the help screen" << endl << endl ;
00489 
00490     bool done = false ;
00491     while( !done )
00492     {
00493         string message = "" ;
00494         size_t len = this->readLine( message ) ;
00495         if( len == -1 || message == "exit" || message == "exit;" )
00496         {
00497             done = true ;
00498         }
00499         else if( message == "help" || message == "help;" || message == "?" )
00500         {
00501             this->displayHelp() ;
00502         }
00503         else if( message.length() > 6 && message.substr( 0, 6 ) == "client" )
00504         {
00505             this->executeCommand( message, 1 ) ;
00506         }
00507         else if( len != 0 && message != "" )
00508         {
00509             CmdTranslation::set_show( false ) ;
00510             try
00511             {
00512                 string doc = CmdTranslation::translate( message ) ;
00513                 if( !doc.empty() )
00514                 {
00515                     this->executeCommand( doc, 1 ) ;
00516                 }
00517             }
00518             catch( BESError &e )
00519             {
00520                 CmdTranslation::set_show( false ) ;
00521                 _isInteractive = false ;
00522                 throw e ;
00523             }
00524             CmdTranslation::set_show( false ) ;
00525         }
00526     }
00527     _isInteractive = false ;
00528 }
00529 
00535 size_t
00536 CmdClient::readLine( string &msg )
00537 {
00538     size_t len = 0 ;
00539     char *buf = (char *) NULL ;
00540     buf =::readline( "BESClient> " ) ;
00541     if( buf && *buf )
00542     {
00543         len = strlen( buf ) ;
00544 #ifdef HAVE_READLINE_HISTORY
00545         add_history( buf ) ;
00546 #endif
00547         if( len > SIZE_COMMUNICATION_BUFFER )
00548         {
00549             cerr << __FILE__ << __LINE__
00550                 <<
00551                 ": incoming data buffer exceeds maximum capacity with lenght "
00552                 << len << endl ;
00553             exit( 1 ) ;
00554         }
00555         else {
00556             msg = buf ;
00557         }
00558     }
00559     else {
00560         if( !buf )
00561         {
00562             // If a null buffer is returned then this means that EOF is
00563             // returned. This is different from the user just hitting enter,
00564             // which means a character buffer is returned, but is empty.
00565 
00566             // Problem: len is unsigned.
00567             len = -1 ;
00568         }
00569     }
00570     if( buf )
00571     {
00572         free( buf ) ;
00573         buf = (char *)NULL ;
00574     }
00575     return len ;
00576 }
00577 
00580 void
00581 CmdClient::displayHelp()
00582 {
00583     cout << endl ;
00584     cout << endl ;
00585     cout << "BES Command Line Client Help" << endl ;
00586     cout << endl ;
00587     cout << "Client commands available:" << endl ;
00588     cout <<
00589         "    exit                     - exit the command line interface" <<
00590         endl ;
00591     cout << "    help                     - display this help screen" <<
00592         endl ;
00593     cout <<
00594         "    client suppress;         - suppress output from the server" <<
00595         endl ;
00596     cout <<
00597         "    client output to screen; - display server output to the screen"
00598         << endl ;
00599     cout <<
00600         "    client output to <file>; - display server output to specified file"
00601         << endl ;
00602     cout <<
00603         "    client load <file>; - load xml document from file"
00604         << endl ;
00605     cout << endl ;
00606     cout <<
00607         "Any commands beginning with 'client' must end with a semicolon" <<
00608         endl ;
00609     cout << endl ;
00610     cout << "To display the list of commands available from the server "
00611         << "please type the command 'show help;'" << endl ;
00612     cout << endl ;
00613     cout << endl ;
00614 }
00615 
00620 bool
00621 CmdClient::isConnected()
00622 {
00623     if( _client )
00624         return _client->isConnected() ;
00625     return false ;
00626 }
00627 
00630 void
00631 CmdClient::brokenPipe()
00632 {
00633     if( _client )
00634         _client->brokenPipe() ;
00635 }
00636 
00643 void
00644 CmdClient::dump( ostream & strm ) const
00645 {
00646     strm << BESIndent::LMarg << "CmdClient::dump - ("
00647         << (void *) this << ")" << endl ;
00648     BESIndent::Indent() ;
00649     if( _client )
00650     {
00651         strm << BESIndent::LMarg << "client:" << endl ;
00652         BESIndent::Indent() ;
00653         _client->dump( strm ) ;
00654         BESIndent::UnIndent() ;
00655     }
00656     else
00657     {
00658         strm << BESIndent::LMarg << "client: null" << endl ;
00659     }
00660     strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl ;
00661     strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl ;
00662     BESIndent::UnIndent() ;
00663 }

Generated on 19 Feb 2010 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.6.1