bes Updated for version 3.20.13
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
48using std::cout;
49using std::endl;
50using std::cerr;
51using std::ofstream;
52using std::ios;
53using std::flush;
54using std::ostringstream;
55using std::ostream;
56using std::string;
57using 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) */
65extern "C" {
66 char *readline(const char *);
67}
68# endif /* !defined(HAVE_READLINE_H) */
69char *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) */
80extern "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
100StandAloneClient::~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
128void 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
213void 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
300void 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
339void 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
414size_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
452void 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
479void 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
Base exception class for the BES with basic string message.
Definition: BESError.h:59
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.