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
Copyright © 2006-2011 Savarese Software Research Corporation. All rights reserved.