Savarese Software Research Corporation
ws_lua/service.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright 2006-2009 Savarese Software Research Corporation
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     https://www.savarese.com/software/ApacheLicense-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include <ssrc/wispers/ws_lua/service.h>
00018 
00019 #ifdef WSPR_DEBUG
00020 #include <ssrc/wispers/utility/PropertiesToString.h>
00021 #endif
00022 
00023 #include <boost/filesystem/path.hpp>
00024 
00025 __BEGIN_NS_SSRC_WSPR_WS_LUA
00026 
00027 using namespace Lua;
00028 using namespace NS_SSRC_WSPR_LUA;
00029 
00030 namespace {
00031   inline void get_parameters(lua_State* state,
00032                              const int index,
00033                              parameter_sequence & parameters)
00034   {
00035     lua_getfield(state, index, "parameters");
00036 
00037     if(lua_istable(state, -1)) {
00038       int count_parameters = 0;
00039 
00040       lua_pushnil(state);
00041       while(lua_next(state, -2)) {
00042         ++count_parameters;
00043         lua_pop(state, 1);
00044       }
00045 
00046       if(count_parameters > 0) {
00047         SSRC_UNIQUE_PTR<CallParameter> param;
00048 
00049         parameters.reserve(count_parameters);
00050 
00051         lua_pushnil(state);
00052         while(lua_next(state, -2)) {
00053           const unsigned int min_size =
00054             get_field<int>(1, state, -1, "min_size");
00055           const unsigned int max_size =
00056             get_field<int>(128, state, -1, "max_size");
00057           const bool multi_value =
00058             get_field<bool>(false, state, -1, "multi_value");
00059           const bool optional = get_field<bool>(false, state, -1, "optional");
00060           const string && pattern =
00061             get_field<string>(state, -1, "matches", "");
00062 
00063           if(pattern.empty()) {
00064             param.reset(new CallParameter(lua_tostring(state, -2),
00065                                           min_size, max_size, multi_value,
00066                                           optional));
00067           } else {
00068             param.reset(new PatternParameter(lua_tostring(state, -2), pattern,
00069                                              min_size, max_size, multi_value,
00070                                              optional));
00071           }
00072 
00073           parameters.push_back(param.release());
00074 
00075           lua_pop(state, 1);
00076         }
00077       }
00078     }
00079 
00080     lua_pop(state, 1);
00081   }
00082 }
00083 
00084 void Service::transition(State state) {
00085   switch(state) {
00086   case Starting:
00087     state = Started;
00088     break;
00089   case Stopping:
00090     state = Stopped;
00091     break;
00092   default:
00093     break;
00094   }
00095 
00096   super::transition(state);
00097 }
00098 
00099 inline void Service::do_call(const WebServiceCall & wc,
00100                              const MessageInfo & msginfo)
00101 {
00102   using NS_SSRC_WSPR_FCGI::parameter_map;
00103   call_map_type::const_iterator call_it = _call_map.find(wc.call);
00104 
00105   if(call_it != _call_map.end()) {
00106     const CallEntry & call = *(call_it->second);
00107     lua_rawgeti(_lua_state, LUA_REGISTRYINDEX, call.ref);
00108 
00109     if(!call.requires_session || wc.session) {
00110       int nargs = 0;
00111 
00112       if(!call.parameters.empty()) {
00113         lua_newtable(_lua_state);
00114         typedef
00115           std::pair<parameter_map::const_iterator,parameter_map::const_iterator>
00116           parameter_range;
00117         parameter_range p;
00118 
00119         // Set parameters
00120          for(parameter_sequence::const_iterator param =
00121               call.parameters.begin(), end = call.parameters.end();
00122             param != end; ++param)
00123         {
00124           if(param->multi_value) {
00125             p = wc.parameters.equal_range(param->name);
00126           } else {
00127             p.first = wc.parameters.find(param->name);
00128             p.second = wc.parameters.end();
00129           }
00130 
00131           if(p.first != p.second) {
00132             if(param->multi_value) {
00133               int pos = 0;
00134               lua_newtable(_lua_state);
00135 
00136               while(p.first != p.second) {
00137                 if(param->validate(p.first->second)) {
00138                   lua_pushstring(_lua_state, p.first->second.c_str());
00139                   lua_rawseti(_lua_state, -2, ++pos);
00140                 } else {
00141                   string error("invalid parameter value: ");
00142                   reply_error(error.append(param->name).append(" = ").append(p.first->second), msginfo);
00143 #ifdef WSPR_DEBUG
00144                   std::cerr << error << std::endl;
00145 #endif
00146                   lua_pop(_lua_state, 2);
00147                   return;
00148                 }
00149                 ++p.first;
00150               }
00151             } else {
00152               if(param->validate(p.first->second)) {
00153                  lua_pushstring(_lua_state, p.first->second.c_str());
00154               } else {
00155                 string error("invalid parameter value: ");
00156                 reply_error(error.append(param->name).append(" = ").append(p.first->second), msginfo);
00157 #ifdef WSPR_DEBUG
00158                 std::cerr << error << std::endl;
00159 #endif
00160                 lua_pop(_lua_state, 2);
00161                 return;
00162               }
00163             }
00164 
00165             lua_setfield(_lua_state, -2, param->name.c_str());
00166           } else if(!param->optional) {
00167             lua_pop(_lua_state, 2);
00168             if(!call.one_way) {
00169               string error("missing parameter: ");
00170               reply_error(error.append(param->name), msginfo);
00171 #ifdef WSPR_DEBUG
00172               std::cerr << error << std::endl;
00173 #endif
00174             }
00175             return;
00176           }
00177         }
00178 
00179         nargs = 1;
00180       }
00181 
00182       /*
00183         TODO: Populate module context (use separate function).  If
00184         modules can perform asynchronous calls completed via
00185         continuations, then we'll need a new module context instance
00186         for each module call, allowing the module call to hold onto
00187         the context for its continuation.
00188        */
00189       MessageResponse response(wc.session, new NS_SSRC_WSPR_UTILITY::Properties);
00190       _context.start_call(&wc, &msginfo, &response);
00191 
00192       if(lua_pcall(_lua_state, nargs, 0, 0) != 0) {
00193 #ifdef WSPR_DEBUG
00194         std::cerr << "ERROR!  " << lua_tostring(_lua_state, -1) << std::endl;
00195 #endif
00196         lua_pop(_lua_state, 1);
00197         // TODO: handle error somehow.
00198       }
00199 
00200       _context.end_call();
00201 
00202       if(!call.one_way) {
00203 #ifdef WSPR_DEBUG
00204         std::cerr << "RESPONSE:\n"
00205                   << NS_SSRC_WSPR_UTILITY::to_string(*response.template_data)
00206                   << std::endl;
00207 #endif
00208         _caller.reply<CallResponse>(msginfo.sender(), msginfo.token(), response, Message::FIFOSelfDiscard);
00209       }
00210 
00211     } else if(!call.one_way) {
00212       string error("valid session required");
00213       reply_error(error, msginfo);
00214 #ifdef WSPR_DEBUG
00215       std::cerr << error << std::endl;
00216 #endif
00217     }
00218   }
00219 }
00220 
00221 void Service::load_module(const string & module_dir, const string & module)
00222   SSRC_DECL_THROW(LoadError)
00223 {
00224   boost::filesystem::path module_path(module_dir);
00225   string module_file(module);
00226   ModuleEntry module_entry;
00227 
00228   module_file.append(".lua");
00229   module_path /= module_file;
00230 
00231   // We don't pop the error string because error is fatal.
00232   if(luaL_dofile(_lua_state, module_path.string().c_str()))
00233     throw LoadError(lua_tostring(_lua_state, -1));
00234 
00235   lua_getglobal(_lua_state, module.c_str());
00236   lua_getfield(_lua_state, -1, "load");
00237 
00238   lua_pushvalue(_lua_state, -2);
00239   lua_pushlightuserdata(_lua_state, &_context);
00240 
00241   if(lua_pcall(_lua_state, 2, 0, 0) != 0)
00242     throw LoadError(lua_tostring(_lua_state, -1));
00243 
00244   lua_getfield(_lua_state, -1, "unload");
00245   if(!lua_isfunction(_lua_state, -1))
00246     throw LoadError("unload() is undefined");
00247   module_entry.unload_ref = luaL_ref(_lua_state, LUA_REGISTRYINDEX);
00248 
00249   lua_getfield(_lua_state, -1, "ws_methods");
00250 
00251   if(lua_istable(_lua_state, -1)) {
00252     lua_pushnil(_lua_state);
00253     while(lua_next(_lua_state, -2)) {
00254       if(lua_istable(_lua_state, -1)) {
00255 
00256         lua_getfield(_lua_state, -1, "call");
00257 
00258         if(lua_isfunction(_lua_state, -1)) {
00259           string call_name = lua_tostring(_lua_state, -3);
00260           SSRC_UNIQUE_PTR<CallEntry> call_entry(new CallEntry(luaL_ref(_lua_state, LUA_REGISTRYINDEX)));
00261 
00262           call_entry->one_way = get_field<bool>(false, _lua_state, -1, "one_way");
00263 
00264           if(call_entry->one_way) {
00265             WS_ONE_WAY_CALL(call_name, &Service::do_call);
00266           } else {
00267             WS_TWO_WAY_CALL(call_name, &Service::do_call);
00268           }
00269 
00270           call_entry->requires_session =
00271             get_field<bool>(true, _lua_state, -1, "requires_session");
00272 
00273           get_parameters(_lua_state, -1, call_entry->parameters);
00274 
00275           if(!_call_map.insert(call_name, call_entry.release()).second) {
00276             string msg("duplicate method name: ");
00277             throw LoadError(msg.append(call_name));
00278           }
00279         } else {
00280           lua_pop(_lua_state, 1);
00281         }
00282       }
00283 
00284       lua_pop(_lua_state, 1);
00285     }
00286   }
00287 
00288   lua_pop(_lua_state, 1);
00289   module_entry.module_ref = luaL_ref(_lua_state, LUA_REGISTRYINDEX);
00290   _modules.push_back(module_entry);
00291 }
00292 
00293 void Service::load_modules(const string & module_dir,
00294                            const module_list & modules)
00295   SSRC_DECL_THROW(LoadError)
00296 {
00297   boost::filesystem::path lua_path(module_dir);
00298 
00299   lua_path /= "?.lua;";
00300 
00301   luaL_openlibs(_lua_state);
00302 
00303   /* Set package.path to module_dir/?.lua. */
00304   lua_getglobal(_lua_state, "package");
00305   lua_getfield(_lua_state, -1, "path");
00306 
00307   string lua_path_str = lua_path.string();
00308   lua_path_str.append(lua_tostring(_lua_state, -1));
00309   lua_pushstring(_lua_state, lua_path_str.c_str());
00310   lua_setfield(_lua_state, -3, "path");
00311   lua_pop(_lua_state, 2);
00312 
00313   for(module_list::const_iterator it = modules.begin(), end = modules.end();
00314       it != end; ++it)
00315   {
00316     load_module(module_dir, *it);
00317   }
00318 }
00319 
00320 void Service::unload_module(const ModuleEntry & module) {
00321   lua_rawgeti(_lua_state, LUA_REGISTRYINDEX, module.unload_ref);
00322   lua_rawgeti(_lua_state, LUA_REGISTRYINDEX, module.module_ref);
00323 
00324   if(lua_pcall(_lua_state, 1, 0, 0) != 0) {
00325 #ifdef WSPR_DEBUG
00326     std::cerr << "unload error: " << lua_tostring(_lua_state, -1)
00327               << std::endl;
00328 #endif
00329     lua_pop(_lua_state, 1);
00330   }
00331 
00332   luaL_unref(_lua_state, LUA_REGISTRYINDEX, module.unload_ref);
00333   luaL_unref(_lua_state, LUA_REGISTRYINDEX, module.module_ref);
00334 }
00335 
00336 void Service::unload_modules() {
00337   // Unload modules in reverse order from which they were loaded.
00338   for(module_entry_list::const_reverse_iterator it = _modules.rbegin(),
00339         end = _modules.rend(); it != end; ++it)
00340   {
00341     unload_module(*it);
00342   }
00343 
00344   // If we're going to support unloading a single module, then
00345   // we have to store the module reference in CallEntry so we
00346   // can unreference all calls that belong to the module.
00347   // For now, however, we simply unload everything at once,
00348   // unreferencing the modules first and then the methods.
00349   for(call_map_type::const_iterator it = _call_map.begin(),
00350         end = _call_map.end(); it != end; ++it)
00351   {
00352     luaL_unref(_lua_state, LUA_REGISTRYINDEX, it->second->ref);
00353   }
00354 
00355   _modules.clear();
00356   _call_map.clear();
00357 }
00358 
00359 __END_NS_SSRC_WSPR_WS_LUA

Savarese Software Research Corporation
Copyright © 2006-2011 Savarese Software Research Corporation. All rights reserved.