OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-cogserver / opencog / cogserver / server / Request.h

274 lines · c ref:
/*
 * opencog/cogserver/server/Request.h
 *
 * Copyright (C) 2008 by OpenCog Foundation
 * Written by Gustavo Gama <gama@vettalabs.com>,
 * Simple-API implementation by Linas Vepstas <linasvepstas@gmail.com>
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 *
 * The Cogserver Request system is deprecated; users are encouraged to
 * explore writing guile (scheme) or python modules instead.
 */


#ifndef _OPENCOG_REQUEST_H
#define _OPENCOG_REQUEST_H

#include <list>
#include <string>

#include <opencog/cogserver/server/Factory.h>

namespace opencog
{
/** \addtogroup grp_server
 *  @{
 */

class CogServer;
class ConsoleSocket;

/**
 * The DECLARE_CMD_REQUEST macro provides a simple, easy-to-use interface
 * to the creation of new modules, while also shielding the module writer
 * from the guts and inner workings of the module loading and command
 * processing system.
 *
 * To be clear: the basic design goals of these macros are:
 *
 * 1) A module implementation that is isolated from the detailed inner
 *    guts and workings of the module loading mechanism. This allows
 *    changes to the module loading and command-request processing 
 *    system to be made, without also having to refactor each and every
 *    module that has ever been created.
 *
 * 2) Individual modules now have a simple, straight-forward interface
 *    for specifying a command, and what should be done in reaction to
 *    that command. In particular, the person creating a new module 
 *    does not have to learn how the command processing system works;
 *    they can focus all their energies on creating the module.
 *
 * 3) A module has only a small number of regular, simple commands that
 *    it needs implemented.  If the module has a large number of
 *    commands, or they have a sophisticated syntax, then using the
 *    class GenericShell will be more suitable.
 */

/**
 * DECLARE_CMD_REQUEST -- Declare command to the command processing system.
 *
 * This wrapper declares a shell command to the loadable module 
 * command processing subsystem. This includes the name of the command,
 * the name of a method to call when the user invokes the command, and
 * the command summary and formatting strings that are printed at the
 * shell prompt.
 *
 * Arguments:
 * - mod_type:Typename of the class that implements the module.
 * - cmd_str: The string name of the command
 * - do_cmd:  Name of the method to call to run the command.
 *            The signature of the method must be as follows (see also
 *            the example, below):
 *            `std::string mod_type::do_cmd(Request *, std::list<std::string>)`
 *            The first arg is the original request; most users will not
 *            need this. The second arg is the parsed command line,
 *            presented as a list of strings.
 * - cmd_sum: A short string to be printed as a command help summary.
 * - cmd_desc:A long help string describing the command in detail.
 * - shell_cmd:A boolean value that indicates the command will create a
 *            shell (REPL loop, e.g. for scheme, python).
 * - hidden:  A boolean value that indicates the command is not listed
 *            when 'help' is used to list commands.
 *
 *
 * Example usage:
 *
 * @code
 * class MyModule: public Module {
 *    private:
 *        DECLARE_CMD_REQUEST(MyModule, "stir-fry", do_stirfry,
 *            "stir-fry:  Make the OpenCog server cook Chinese food.",
 *            "Usage: stir-fry <rice> <sesame-oil> <etc>\n\n"
 *            "This is a very super-intellgent cooking command.\n"
 *            "It even makes coffee!!!",
 *            false, false)
 * };
 *
 * MyModule::MyModule() {       // In the constructor, or during init
 *     do_stirfry_register();   // The command must be registered!
 * }
 *
 * MyModule::~MyModule() {       // In the destructor, or earlier if desired
 *     do_stirfry_unregister();  // The command must be unregistered!
 * }
 *
 * std::string MyModule::do_stirfry(Request *r, std::list<std::string> args) {
 *     if (args.size() != 23)
 *         return "stirfry: Error: 23 ingredients must be specified";
 *
 *     std::string first_ingredient = args.front(); args.pop_front();
 *
 *     // Now do some cooking ...
 *
 *     return "Your meal is now ready to be eaten!";
 * }
 * @endcode
 *
 * The above is all there's to it! Just register and unregister the 
 * commands with the command processing subsystem, implement the "do"
 * routine, and go. A module may declare as many commands as desired.
 * Be sure to register and unregister each command.
 */
#define DECLARE_CMD_REQUEST(mod_type,cmd_str,do_cmd,                  \
                            cmd_sum,cmd_desc,shell_cmd,hidden)        \
                                                                      \
   class do_cmd##Request : public Request {                           \
      public:                                                         \
          static inline const RequestClassInfo& info(void) {          \
              static const RequestClassInfo _cci(cmd_str,             \
                                                 cmd_sum,             \
                                                 cmd_desc,            \
                                                 shell_cmd,           \
                                                 hidden);             \
              return _cci;                                            \
          }                                                           \
          do_cmd##Request(CogServer& cs) : Request(cs) {};            \
          virtual ~do_cmd##Request() {};                              \
          virtual bool execute(void) {                                \
              logger().debug("[ %s Request] execute", cmd_str);       \
                                                                      \
              mod_type* mod =                                         \
                  static_cast<mod_type *>(_cogserver.getModule(       \
                                          #mod_type));                \
                                                                      \
              std::string rs = mod->do_cmd(this, _parameters);        \
              send(rs);                                               \
              return true;                                            \
          }                                                           \
          virtual bool isShell(void) {                                \
              return info().is_shell;                                 \
          }                                                           \
    };                                                                \
                                                                      \
    /* Declare the factory to manage this request */                  \
    Factory<do_cmd##Request, Request> do_cmd##Factory;                \
                                                                      \
    /* Declare the method that performs the actual action */          \
    std::string do_cmd(Request *, std::list<std::string>);            \
                                                                      \
    /* Declare routines to register and unregister the factories */   \
    void do_cmd##_register(void) {                                    \
        CogServerNodeCast(_hcsn)->registerRequest(do_cmd##Request::info().id, \
                                    & do_cmd##Factory);               \
    }                                                                 \
    void do_cmd##_unregister(void) {                                  \
        CogServerNodeCast(_hcsn)->unregisterRequest(do_cmd##Request::info().id); \
    }


/**
 * This class defines the abstract base class that should be extended
 * by all cogserver command requests. It provides common members used
 * by most requests, such as the list of request parameters.
 *
 * A typical derived request only has to override/implement two methods:
 * 'info' and 'execute'.
 *
 * Since requests are registered with the cogserver using the
 * Registry+Factory pattern, request classes must implement a static
 * 'info' method which uniquely identifies its class. Note that the
 * Request class uses an extended 'info' class (RequestClassInfo)
 * which should add a description attribute and some help text about
 * the request's usage.
 *
 * The 'execute' method must be overridden by derived requests and
 * implement the actual request behavior. It should retrieve the set of
 * parameters from the '_parameters' member and use the 'send()' method
 * to send its output (or an error message) back to the client.
 *
 * A typical derived Request declaration and initialization would thus
 * look as follows:
 *
 * @code
 * // MyRequest.h
 * #include <opencog/cogserver/server/Request.h>
 * #include <opencog/cogserver/server/Factory.h>
 * class CogServer;
 * class MyRequest : public opencog::Request {
 *     static inline const RequestClassInfo& info() {
 *         static const RequestClassInfo _cci(
 *             "myrequest",
 *             "description of request 'myrequest'",
 *             "myrequest -a <param1>\n"
 *             "myrequest -d <param2>\n"
 *             "myrequest [<optional param 3>]\n"
 *         );
 *         return _cci;
 *     }
 *
 *     bool execute() {
 *         std::ostringstream oss;
 *         // implement the request's behavior
 *         ...
 *         oss << 'command output';
 *         ...
 *         send(oss.str());
 *         return true;
 *     }
 * }
 *
 * // application/module code
 * #include "MyRequest.h"
 * #include <opencog/cogserver/server/Request.h>
 * #include <opencog/cogserver/server/CogServer.h>
 * ...
 * Factory<MyRequest, Request> factory;
 * CogServer& cogserver = static_cast<CogServer&>(server());
 * cogserver.registerRequest(MyRequest::info().id, &factory); 
 * ...
 * @endcode
 */
class Request
{
private:
    ConsoleSocket*         _console;

protected:
    CogServer&             _cogserver;
    std::list<std::string> _parameters;

public:
    /** Request's constructor */
    Request(CogServer&);

    /** Request's desconstructor */
    virtual ~Request();

    /** Abstract execute method. Should be overridden by a derived request with
     *  the actual request's behavior. Returns 'true' if the command completed
     *  successfully and 'false' otherwise. */
    virtual bool execute(void) = 0;

    /** Return true, if running the Request will create and enter a shell. */
    virtual bool isShell(void) = 0;

    /** Send the command output back to the client. */
    void send(const std::string& msg) const;

    /** Stores the socket on which to return results. */
    void set_console(ConsoleSocket*);
    ConsoleSocket *get_console(void) const { return _console; }

    /** sets the command's parameter list. */
    virtual void setParameters(const std::list<std::string>&);

    /** adds a parameter to the commands parameter list. */
    virtual void addParameter(const std::string&);
};

/** @}*/
} // namespace 

#endif // _OPENCOG_REQUEST_H