PLearn 0.1
PLearnServer.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // PLearnServer.cc
00004 //
00005 // Copyright (C) 2005 Pascal Vincent 
00006 // Copyright (C) 2007 Xavier Saint-Mleux, ApSTAT Technologies inc.
00007 // 
00008 // Redistribution and use in source and binary forms, with or without
00009 // modification, are permitted provided that the following conditions are met:
00010 // 
00011 //  1. Redistributions of source code must retain the above copyright
00012 //     notice, this list of conditions and the following disclaimer.
00013 // 
00014 //  2. Redistributions in binary form must reproduce the above copyright
00015 //     notice, this list of conditions and the following disclaimer in the
00016 //     documentation and/or other materials provided with the distribution.
00017 // 
00018 //  3. The name of the authors may not be used to endorse or promote
00019 //     products derived from this software without specific prior written
00020 //     permission.
00021 // 
00022 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00023 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00024 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00025 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00026 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00027 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00028 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00029 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00030 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00031 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 // 
00033 // This file is part of the PLearn library. For more information on the PLearn
00034 // library, go to the PLearn Web site at www.plearn.org
00035 
00036 /* *******************************************************      
00037  * $Id: PLearnServer.cc 9130 2008-06-16 18:21:54Z saintmlx $ 
00038  ******************************************************* */
00039 
00040 // Authors: Pascal Vincent
00041 
00045 #include "PLearnServer.h"
00046 #include <plearn/base/RemoteDeclareMethod.h>
00047 #include <plearn/base/pl_repository_revision.h>
00048 #include <plearn/base/Object.h>
00049 #include <plearn/base/plerror.h>
00050 #include <plearn/io/fileutils.h> 
00051 #include <plearn/io/load_and_save.h>
00052 #include <plearn/io/pl_log.h>
00053 #include <plearn/math/random.h>
00054 #include <plearn/io/PyPLearnScript.h> // For smartLoadObject
00055 #include <plearn/io/ServerLogStreamBuf.h> 
00056 
00057 namespace PLearn {
00058 using namespace std;
00059 
00060 // Put function implementations here.
00061 
00062 PLearnServer* PLearnServer::instance = 0;
00063 
00064 PLearnServer::PLearnServer(const PStream& input_output)
00065     :io(input_output), clear_maps(true)
00066 {
00067     io.remote_plearn_comm= true;
00068     if(instance!=0)
00069         PLERROR("An instance of PLearnServer already exists");
00070     instance = this;
00071 }
00072 
00073 PLearnServer::~PLearnServer()
00074 {
00075     io.remote_plearn_comm= false;
00076     instance = 0;
00077 }
00078 
00079 PLearnServer* PLearnServer::getInstance()
00080 { 
00081     return instance; 
00082 }
00083 
00084 
00085 void PLearnServer::cd(const string path)
00086 {
00087     chdir(path);
00088 }
00089 
00090 void PLearnServer::binary()
00091 {
00092     getInstance()->io.setMode(PStream::plearn_binary);
00093 }
00094 
00095 void PLearnServer::ascii()
00096 {
00097     getInstance()->io.setMode(PStream::plearn_ascii);
00098 }
00099 
00100 void PLearnServer::implicit_storage(bool impl_stor)
00101 {
00102     getInstance()->io.implicit_storage = impl_stor;
00103 }
00104 
00105 void PLearnServer::loggingControl(int vlevel, TVec<string> modules)
00106 {
00107     PL_Log::instance().verbosity(vlevel);
00108     PL_Log::instance().enableNamedLogging(modules);
00109 }
00110 
00111 void PLearnServer::setOptionLevel(const OptionBase::OptionLevel& level)
00112 {
00113     OptionBase::setCurrentOptionLevel(level);
00114 }
00115 
00116 BEGIN_DECLARE_REMOTE_FUNCTIONS
00117 
00118 declareFunction("cd", &PLearnServer::cd,
00119                 (BodyDoc("change directory (calls chdir)\n"),
00120                  ArgDoc ("path", "Path of directory where to go")));
00121 
00122 declareFunction("binary", &PLearnServer::binary,
00123                 (BodyDoc("change the mode of the io of the PLearnServer instance to plearn_binary \n")));
00124 
00125 declareFunction("ascii", &PLearnServer::ascii,
00126                 (BodyDoc("change the mode of the io of the PLearnServer instance to plearn_ascii\n")));
00127 
00128 declareFunction("implicit_storage", &PLearnServer::implicit_storage,
00129                 (BodyDoc("change the implicit_storage mode of the io of the PLearnServer instance.\n"),
00130                  ArgDoc ("impl_stor", "Whether or not to use implicit_storage")));
00131 
00132 declareFunction("loggingControl", &PLearnServer::loggingControl,
00133                 (BodyDoc("Set current logging level and modules.\n"),
00134                  ArgDoc ("vlevel","the verbosity level"),
00135                  ArgDoc ("modules","list of modules names to log")));
00136 
00137 declareFunction("setOptionLevel", &PLearnServer::setOptionLevel,
00138                 (BodyDoc("Set current option level.\n"),
00139                  ArgDoc ("level","option level")));
00140 
00141 END_DECLARE_REMOTE_FUNCTIONS
00142 
00143 
00144 void PLearnServer::callFunction(const string& name, int nargs)
00145 {
00146     DBG_LOG << "PLearnServer FUNCTION CALL name=" << name << " nargs=" << nargs << endl;
00147     if(name=="cd")
00148     {
00149         string path;
00150         io >> path;
00151         DBG_LOG << "  Arg0 = " << name << endl;        
00152         chdir(path);
00153         Object::prepareToSendResults(io,0);
00154     }
00155     else if(name=="binary")
00156     {
00157         io.setMode(PStream::plearn_binary);
00158         Object::prepareToSendResults(io,0);
00159     }
00160     else if(name=="ascii")
00161     {
00162         io.setMode(PStream::plearn_ascii);
00163         Object::prepareToSendResults(io,0);
00164     }
00165     else if(name=="implicit_storage") // Takes a single boolean parameter
00166     {
00167         io >> io.implicit_storage;
00168         Object::prepareToSendResults(io,0);
00169     }
00170     else if(name=="pl_repository_revision") // returns the pl_repository_revision string
00171     {
00172         string revs = pl_repository_revision();
00173         Object::prepareToSendResults(io,1);
00174         io << revs;
00175     }
00176     else
00177     {
00178         PLearn::callFunction(name, nargs, io);
00179     }
00180     io << endl;
00181     DBG_LOG << "-> FUNCTION CALL DONE." << endl;
00182 }
00183 
00184 
00185 void PLearnServer::printHelp()
00186 {
00187     io.write("Summary of commands:\n"
00188              "  !?                                 # print this help message \n"
00189              "  !F functionname nargs arg1 ...     # calls a supported function.\n"
00190              "  !N objid object_specification      # creates new object.\n"
00191              "  !L objid filepath                  # loads a new object into id from a .plearn .psave .vmat file\n"
00192              "  !M objid methodname nargs arg1 ... # calls method on object objid. Returns: !R <nreturn> ret1 ... \n"
00193              "  !D objid                   # deletes object objid. Returns: !R 0 \n"
00194              "  !Z                         # delete all objects. Returns: !R 0 \n"
00195              "  !P                         # Ping. Returns: !R 0 \n"
00196              "  !Q                         # Quit. Returns nothing. \n"
00197              "  !K                         # Kill server and quit. Returns nothing. \n"
00198              "\n"
00199              "Except for ?, Q and K, all commands upon success return: \n"
00200              "  !R n_return_args arg1 ... \n"
00201              "If an error or exception occurs, the following is returned: \n"
00202              "  !E \"errormsg\" \n"
00203              "\n"
00204              "Summary of currently supported functions:\n"
00205              "  !F cd 1 \"path\" \n\n"
00206              "OBSOLETE: Advanced technical note: objects with objid>=10000 are also inserted in the stream's copies_map\n"
00207              "so that they may be referenced as arguments to method or funtion calls, for ex as: *10001; \n"
00208              "\n"
00209         );
00210     io << endl;
00211 }
00212 
00213 
00214 bool PLearnServer::run()
00215 {
00216     int obj_id;
00217     Object* obj;
00218     ObjMap::iterator found;
00219     string method_name;
00220     int n_args; // number of input arguments to the method call
00221     string filepath;
00222 
00223     // forward log messages to client
00224     PP<PL_LogPlugin> orig_log_plugin= PL_Log::instance().getCurrentPlugin();
00225     PL_Log::instance().setPlugin(new PL_LogPluginServer(io));
00226     // forward progress messages to client, unless the pbar plugin is null
00227     PP<ProgressBarPlugin> orig_pb_plugin= ProgressBar::getCurrentPlugin();
00228     if(dynamic_cast<NullProgressBarPlugin*>(static_cast<ProgressBarPlugin*>(orig_pb_plugin)) == 0)
00229         ProgressBar::setPlugin(new RemoteProgressBarPlugin(io));
00230     // forward pout&perr to client
00231     PStream orig_pout= pout;
00232     PStream orig_perr= perr;
00233     pout= new ServerLogStreamBuf(io, "pout", VLEVEL_NORMAL);
00234     perr= new ServerLogStreamBuf(io, "perr", VLEVEL_NORMAL);
00235 
00236     DBG_LOG << "ENTERING PLearnServer::run()" << endl;
00237 
00238     for(;;)
00239     {
00240         if(clear_maps)
00241         {
00242             io.copies_map_in.clear();
00243             io.copies_map_out.clear();
00244             for (ObjMap::iterator it = objmap.begin(); it != objmap.end(); ++it)
00245                 io.copies_map_in[it->first]= it->second;
00246         }
00247         int c = -1;
00248         do 
00249             c = io.get(); 
00250         while(io.good() && c!='!' && c!=EOF);
00251         
00252         if(c==EOF || !io)
00253             return true;
00254         int command = io.get();
00255         
00256         try 
00257         {            
00258             switch(command)
00259             {
00260             case '?':
00261                 printHelp();
00262                 break;
00263              
00264             case 'P': // ping
00265                 Object::prepareToSendResults(io,0);
00266                 io << endl;
00267                 break;
00268 
00269             case 'F': // call function 
00270                 io >> method_name >> n_args;
00271                 callFunction(method_name, n_args);
00272                 io << endl;
00273                 break;
00274 
00275             case 'N': // new
00276                 DBG_LOG << "PLearnServer NEW OBJECT" << endl;
00277                 obj = 0;
00278                 io >> obj_id >> obj;           // Read new object
00279                 DBG_LOG << "  obj_id = " << obj_id << endl;
00280                 objmap[obj_id] = obj;
00281                 Object::prepareToSendResults(io,0);
00282                 io << endl;  
00283                 DBG_LOG << "-> OBJECT CREATED." << endl;
00284                 break;
00285 
00286             case 'O': // new w/o id; id is returned
00287                 DBG_LOG << "PLearnServer NEW OBJECT w/o ID" << endl;
00288                 obj = 0;
00289                 io >> obj;           // Read new object
00290                 obj_id= findFreeObjID();
00291                 DBG_LOG << "  obj_id = " << obj_id << endl;
00292                 objmap[obj_id] = obj;
00293                 Object::prepareToSendResults(io,1);
00294                 io << obj_id << endl;  
00295                 DBG_LOG << "-> OBJECT CREATED." << endl;
00296                 break;
00297             
00298             case 'L': // load from file
00299                 DBG_LOG << "PLearnServer LOAD OBJECT" << endl;
00300                 obj = 0;
00301                 io >> obj_id >> filepath;
00302                 DBG_LOG << "  obj_id = " << obj_id << endl;
00303                 DBG_LOG << "  filepath = " << filepath << endl;                
00304                 // PLearn::load(filepath,obj);
00305                 obj = smartLoadObject(filepath);
00306                 objmap[obj_id] = obj;
00307                 Object::prepareToSendResults(io,0);
00308                 io << endl;  
00309                 DBG_LOG << "-> OBJECT LOADED." << endl;
00310                 break;
00311 
00312             case 'D': // delete
00313                 DBG_LOG << "PLearnServer DELETE OBJECT" << endl;
00314                 io >> obj_id;
00315                 DBG_LOG << "  ojbj_id = " << obj_id << endl;
00316                 if(objmap.erase(obj_id)==0)
00317                     PLERROR("Calling delete of a non-existing object");
00318                 Object::prepareToSendResults(io,0);
00319                 io << endl;
00320                 DBG_LOG << "-> OBJECT DELETED." << endl;
00321                 break;
00322 
00323             case 'M': // method call
00324                 DBG_LOG << "PLearnServer METHOD CALL" << endl;
00325                 io >> obj_id;
00326                 DBG_LOG << "  ojbj_id = " << obj_id << endl;
00327                 found = objmap.find(obj_id);
00328                 DBG_LOG << "objmap= " << objmap << endl;
00329                 DBG_LOG << "cmo= " << io.copies_map_out << endl;
00330                 if(found == objmap.end()) // unexistant obj_id
00331                     PLERROR("Calling a method on a non-existing object");
00332                 else 
00333                 {
00334                     io >> method_name >> n_args;
00335                     DBG_LOG << "  method_name = " << method_name << endl;
00336                     DBG_LOG << "  n_args = " << n_args << endl;
00337                     // cerr << "Method: " << method_name << ' ' << n_args << endl;
00338                     found->second->call(method_name, n_args, io);
00339                     io << endl;
00340                     DBG_LOG << "-> METHOD CALL DONE." << endl;
00341                 }
00342                 break;
00343 
00344             case 'Z': // delete all objects
00345                 DBG_LOG << "PLearnServer DELETE ALL OBJECTS" << endl;
00346                 objmap.clear();
00347                 Object::prepareToSendResults(io,0);
00348                 io << endl;
00349                 DBG_LOG << "-> ALL OBJECTS DELETED." << endl;
00350                 break;
00351 
00352             case 'Q': // quit
00353                 PL_Log::instance().setPlugin(orig_log_plugin);
00354                 ProgressBar::setPlugin(orig_pb_plugin);
00355                 pout= orig_pout;
00356                 perr= orig_perr;
00357                 io.setMode(PStream::plearn_ascii);
00358                 DBG_LOG << "PLearnServer QUIT" << endl;
00359                 // cerr << "Quitting" << endl;
00360                 DBG_LOG << "LEAVING PLearnServer::run()" << endl;
00361                 return true;
00362               
00363             case 'K': // kill
00364                 DBG_LOG << "PLearnServer KILL" << endl;
00365                 DBG_LOG << "LEAVING PLearnServer::run()" << endl;
00366                 return false;
00367 
00368             default:
00369                 PLERROR("Invalid PLearnServer command char: %c Type !? for help.",(char)command);
00370                 return true;
00371             }
00372         }
00373         catch(const PLearnError& e)
00374         {
00375             // cerr << "PLearnServer caught PLearnError \"" << e.message() << '"' << endl;
00376             try 
00377             {
00378                 io.write("!E ");
00379                 io << e.message() << endl;
00380             }
00381             catch(const PLearnError& e2)
00382             {
00383                 IMP_LOG << "PLearnServer ERROR: " << e2.message() << endl
00384                         << " while trying to send (to io) error: " << e.message() << endl
00385                         << " Probably due to peer closing before we finished sending." << endl
00386                         << " (If, as is likely, what we were sending were some remaining blanks" << endl
00387                         << " no need to worry...)." << endl;
00388             }
00389         }
00390         catch (...) 
00391         {
00392             try
00393             {
00394                 io.write("!E ");
00395                 io << "Unknown exception" << endl;
00396             }
00397             catch(const PLearnError& e2)
00398             {
00399                 IMP_LOG << "PLearnServer ERROR: " << e2.message() << endl
00400                         << " while trying to send (to io) notification of unknown exception." << endl
00401                         << " Probably due to peer closing before we finished sending." << endl
00402                         << " (If, as is likely, what we were sending were some remaining blanks" << endl
00403                         << " no need to worry...)." << endl;
00404             }
00405         }
00406     }
00407     DBG_LOG << "LEAVING PLearnServer::run()" << endl;
00408     return true;
00409 }
00410 
00411 int PLearnServer::findFreeObjID() const
00412 {
00413     //DUMMY method that tries to find an unused ID
00414     // this algorithm is not guaranteed to work... use at your own risk or modify accordingly
00415     int id;
00416     const int maxtries= 65536;
00417     int ntries= 0;
00418     do
00419         id= static_cast<int>(bounded_uniform(0,2000000000));
00420     while(objmap.find(id) != objmap.end() && ++ntries < maxtries);
00421     if(ntries >= maxtries)
00422         PLERROR("PLearnServer::findFreeObjID : can't find a suitable ID within %d tries.", maxtries);
00423     return id;
00424 }
00425 
00426 
00427 } // end of namespace PLearn
00428 
00429 
00430 /*
00431   Local Variables:
00432   mode:c++
00433   c-basic-offset:4
00434   c-file-style:"stroustrup"
00435   c-file-offsets:((innamespace . 0)(inline-open . 0))
00436   indent-tabs-mode:nil
00437   fill-column:79
00438   End:
00439 */
00440 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines