PLearn 0.1
|
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 :