webmcp

annotate framework/bin/mcp.lua @ 317:732c4d53a823

Invoke Moonbridge's listen function without delay (requires passing http_options as argument); Code cleanup
author jbe
date Mon Mar 23 19:05:32 2015 +0100 (2015-03-23)
parents a2c733535b8e
children f5660406ad3b
rev   line source
jbe@203 1 #!/usr/bin/env moonbridge
jbe/bsw@0 2
jbe@294 3 --[[--
jbe@294 4 WEBMCP_VERSION
jbe@294 5
jbe@294 6 A string containing the WebMCP version, e.g. "2.0.0"
jbe@294 7 --]]--
jbe@278 8 WEBMCP_VERSION = "2.0.0"
jbe@294 9 --//--
jbe@64 10
jbe@317 11 --[[--
jbe@317 12 _G
jbe@317 13
jbe@317 14 A reference to the global namespace. To avoid accidental programming errors, global variables cannot be set directly, but they must be set through the _G reference, e.g. use _G.foo = true to set the variable "foo" to a value of true.
jbe@317 15
jbe@317 16 The only exception is the <framework>/env/__init.lua file and the <application>/env/__init.lua file, in which global variables may be set without using the _G reference.
jbe@317 17
jbe@317 18 Note that the global namespace may or may not be shared between requests (Moonbridge creates multiple forks of the Lua machine). To set variables that are to be cleared after the request has been finished, an application may use the "app" table, e.g. app.foo = true to set the variable app.foo to a value of true, which will be cleared automatically when the request has ended.
jbe@317 19
jbe@317 20 --]]--
jbe@231 21 local _G = _G
jbe@237 22 local allowed_globals = {}
jbe@231 23 local global_metatable = {
jbe@231 24 __index = _G,
jbe@231 25 __newindex = function(self, key, value)
jbe@231 26 _G[key] = value
jbe@231 27 end
jbe@231 28 }
jbe@317 29 _ENV = setmetatable(
jbe@317 30 {}, -- proxy environment used within mcp.lua and by all chunks loaded through loadcached(...)
jbe@317 31 global_metatable
jbe@317 32 )
jbe@317 33 local function protect_globals() -- called before first configuration file is loaded
jbe@317 34 function global_metatable.__newindex(self, key, value)
jbe@317 35 if allowed_globals[key] then
jbe@317 36 _G[key] = value
jbe@317 37 else
jbe@317 38 if type(key) == "string" and string.match(key, "^[A-Za-z_][A-Za-z_0-9]*$") then
jbe@317 39 error('Attempt to set global variable "' .. key .. '" (hint: use _G.' .. key .. '=<value> to override protection mechnamism)', 2)
jbe@317 40 else
jbe@317 41 error('Attempt to set global variable', 2)
jbe@317 42 end
jbe@317 43 end
jbe@317 44 end
jbe@317 45 end
jbe@231 46
jbe@294 47 --[[--
jbe@309 48 lua_func = -- compiled Lua function
jbe@309 49 loadcached(
jbe@309 50 filename -- path to a Lua source or byte-code file
jbe@309 51 )
jbe@309 52
jbe@309 53 Loads, compiles and caches a Lua chunk. If the file does not exist, nil and an error string are returned. Otherwise the file is loaded, compiled, and cached. The cached value (i.e. the compiled function) is returned. An error is raised if the compilation was not successful.
jbe@309 54
jbe@309 55 --]]--
jbe@309 56 do
jbe@309 57 local cache = {}
jbe@309 58 function loadcached(filename)
jbe@311 59 local cached_func = cache[filename]
jbe@311 60 if cached_func then
jbe@311 61 return cached_func
jbe@311 62 end
jbe@309 63 local file, read_error = io.open(filename, "r")
jbe@309 64 if file then
jbe@309 65 local filedata = assert(file:read("*a"))
jbe@309 66 assert(file:close())
jbe@309 67 local func, compile_error = load(filedata, "=" .. filename, nil, _ENV)
jbe@309 68 if func then
jbe@309 69 cache[filename] = func
jbe@309 70 return func
jbe@309 71 end
jbe@311 72 error(compile_error, 0)
jbe@309 73 else
jbe@309 74 return nil, read_error
jbe@309 75 end
jbe@309 76 end
jbe@309 77 end
jbe@309 78 --//--
jbe@309 79
jbe@309 80 --[[--
jbe@294 81 WEBMCP_MODE
jbe@294 82
jbe@294 83 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode.
jbe@294 84 --]]--
jbe@203 85 if listen then -- defined by moonbridge
jbe@203 86 WEBMCP_MODE = "listen"
jbe@203 87 else
jbe@203 88 WEBMCP_MODE = "interactive"
jbe@64 89 end
jbe@294 90 --//--
jbe@203 91
jbe@294 92 --[[--
jbe@294 93 WEBMCP_CONFIG_NAMES
jbe@294 94
jbe@294 95 A list of the selected configuration names.
jbe@294 96 --]]--
jbe@294 97 -- configuration names are provided as 4th, 5th, etc. command line argument
jbe@206 98 WEBMCP_CONFIG_NAMES = {select(4, ...)}
jbe@294 99 --//--
jbe@294 100
jbe@294 101 --[[--
jbe@294 102 WEBMCP_FRAMEWORK_PATH
jbe@294 103
jbe@294 104 Directory of the WebMCP framework (always includes a trailing slash).
jbe@294 105 --]]--
jbe@294 106 -- set in mcp.lua
jbe@294 107 --//--
jbe@294 108
jbe@294 109 --[[--
jbe@294 110 WEBMCP_BASE_PATH
jbe@294 111
jbe@294 112 Base directory of the application (always includes a trailing slash).
jbe@294 113 --]]--
jbe@294 114 -- set in mcp.lua
jbe@294 115 --//--
jbe@294 116
jbe@294 117 --[[--
jbe@294 118 WEBMCP_APP_NAME
jbe@294 119
jbe@294 120 Application name (usually "main"). May be nil in case of interactive mode.
jbe@294 121 --]]--
jbe@294 122 -- set in mcp.lua
jbe@294 123 --//--
jbe@203 124
jbe@203 125 -- determine framework and bath path from command line arguments
jbe@203 126 -- or print usage synopsis (if applicable)
jbe@68 127 do
jbe@206 128 local arg1, arg2, arg3 = ...
jbe@203 129 local helpout
jbe@203 130 if
jbe@203 131 arg1 == "-h" or arg1 == "--help" or
jbe@206 132 arg2 == "-h" or arg2 == "--help" -- if first arg is provided by wrapper
jbe@203 133 then
jbe@203 134 helpout = io.stdout
jbe@316 135 elseif #WEBMCP_CONFIG_NAMES < 1 then
jbe@203 136 helpout = io.stderr
jbe@203 137 end
jbe@203 138 if helpout then
jbe@316 139 helpout:write("Usage: moonbridge [moonbr opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
jbe@316 140 helpout:write(" or: lua -i [Lua opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
jbe@203 141 if helpout == io.stderr then
jbe@203 142 return 1
jbe@68 143 else
jbe@203 144 return 0
jbe@68 145 end
jbe@68 146 end
jbe@203 147 local function append_trailing_slash(str)
jbe@217 148 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
jbe@203 149 end
jbe@203 150 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
jbe@203 151 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
jbe@316 152 WEBMCP_APP_NAME = arg3
jbe@68 153 end
jbe@1 154
jbe@313 155 -- check if framework path is correct
jbe@313 156 do
jbe@313 157 local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
jbe@313 158 if not file then
jbe@313 159 error('Could not find "webmcp_version" file: ' .. errmsg, 0)
jbe@313 160 end
jbe@313 161 local version = assert(file:read())
jbe@313 162 assert(file:close())
jbe@313 163 if version ~= WEBMCP_VERSION then
jbe@313 164 error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
jbe@313 165 end
jbe@313 166 end
jbe@313 167
jbe@203 168 -- setup search paths for libraries
jbe/bsw@0 169 do
jbe@217 170 if string.match(package.path, "^[^;]") then
jbe@217 171 package.path = ";" .. package.path
jbe@217 172 end
jbe@217 173 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
jbe/bsw@0 174 -- find out which file name extension shared libraries have
jbe/bsw@0 175 local slib_exts = {}
jbe/bsw@0 176 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
jbe@217 177 if not slib_exts[ext] then
jbe@217 178 slib_exts[#slib_exts+1] = ext
jbe@217 179 slib_exts[ext] = true
jbe@217 180 end
jbe/bsw@0 181 end
jbe/bsw@0 182 local paths = {}
jbe@217 183 for i, ext in ipairs(slib_exts) do
jbe@203 184 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
jbe/bsw@0 185 end
jbe@217 186 for i, ext in ipairs(slib_exts) do
jbe@203 187 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
jbe/bsw@0 188 end
jbe/bsw@0 189 paths[#paths+1] = package.cpath
jbe/bsw@0 190 package.cpath = table.concat(paths, ";")
jbe/bsw@0 191 end
jbe/bsw@0 192
jbe@203 193 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
jbe@203 194 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
jbe@203 195 -- and models "$WEBMCP_BASE_PATH/model/"
jbe@317 196 local root_init -- function which executes the __init.lua file in the environment's root
jbe/bsw@0 197 do
jbe/bsw@0 198 local weakkey_mt = { __mode = "k" }
jbe/bsw@0 199 local autoloader_category = setmetatable({}, weakkey_mt)
jbe/bsw@0 200 local autoloader_path = setmetatable({}, weakkey_mt)
jbe/bsw@0 201 local autoloader_mt = {}
jbe@219 202 local function install_autoloader(self, category, path_fragment)
jbe/bsw@0 203 autoloader_category[self] = category
jbe@219 204 autoloader_path[self] = path_fragment
jbe/bsw@0 205 setmetatable(self, autoloader_mt)
jbe/bsw@0 206 end
jbe/bsw@0 207 local function try_exec(filename)
jbe@309 208 local func = loadcached(filename)
jbe@309 209 if func then
jbe@309 210 func()
jbe@309 211 return true
jbe/bsw@0 212 else
jbe/bsw@0 213 return false
jbe/bsw@0 214 end
jbe/bsw@0 215 end
jbe/bsw@0 216 function autoloader_mt.__index(self, key)
jbe/bsw@0 217 local category, base_path, merge_base_path, file_key
jbe/bsw@0 218 local merge = false
jbe/bsw@0 219 if
jbe/bsw@0 220 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
jbe/bsw@0 221 not string.find(key, "^__")
jbe/bsw@0 222 then
jbe/bsw@0 223 category = "env"
jbe@203 224 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
jbe/bsw@0 225 merge = true
jbe@203 226 merge_base_path = WEBMCP_BASE_PATH .. "env/"
jbe/bsw@0 227 file_key = key
jbe/bsw@0 228 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
jbe/bsw@0 229 category = "model"
jbe@203 230 base_path = WEBMCP_BASE_PATH .. "model/"
jbe/bsw@0 231 local first = true
jbe/bsw@0 232 file_key = string.gsub(key, "[A-Z]",
jbe/bsw@0 233 function(c)
jbe/bsw@0 234 if first then
jbe/bsw@0 235 first = false
jbe/bsw@0 236 return string.lower(c)
jbe/bsw@0 237 else
jbe/bsw@0 238 return "_" .. string.lower(c)
jbe/bsw@0 239 end
jbe/bsw@0 240 end
jbe/bsw@0 241 )
jbe/bsw@0 242 else
jbe/bsw@0 243 return
jbe/bsw@0 244 end
jbe/bsw@0 245 local required_category = autoloader_category[self]
jbe/bsw@0 246 if required_category and required_category ~= category then return end
jbe@219 247 local path_fragment = autoloader_path[self]
jbe@219 248 local path = base_path .. path_fragment .. file_key
jbe@219 249 local merge_path
jbe/bsw@0 250 if merge then
jbe@219 251 merge_path = merge_base_path .. path_fragment .. file_key
jbe/bsw@0 252 end
jbe/bsw@0 253 local function try_dir(dirname)
jbe/bsw@0 254 local dir = io.open(dirname)
jbe/bsw@0 255 if dir then
jbe/bsw@0 256 io.close(dir)
jbe/bsw@0 257 local obj = {}
jbe@219 258 install_autoloader(obj, category, path_fragment .. file_key .. "/")
jbe/bsw@0 259 rawset(self, key, obj)
jbe@219 260 try_exec(path .. "/__init.lua")
jbe@219 261 if merge then try_exec(merge_path .. "/__init.lua") end
jbe/bsw@0 262 return true
jbe/bsw@0 263 else
jbe/bsw@0 264 return false
jbe/bsw@0 265 end
jbe/bsw@0 266 end
jbe@238 267 if self == _G then
jbe@237 268 allowed_globals[key] = true
jbe@233 269 end
jbe@219 270 if merge and try_exec(merge_path .. ".lua") then
jbe@219 271 elseif merge and try_dir(merge_path .. "/") then
jbe@219 272 elseif try_exec(path .. ".lua") then
jbe@219 273 elseif try_dir(path .. "/") then
jbe/bsw@0 274 else end
jbe@238 275 if self == _G then
jbe@237 276 allowed_globals[key] = nil
jbe@233 277 end
jbe@237 278 return rawget(self, key)
jbe/bsw@0 279 end
jbe@219 280 install_autoloader(_G, nil, "")
jbe@317 281 function root_init() -- upvalue
jbe@317 282 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
jbe@317 283 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
jbe@317 284 end
jbe@214 285 end
jbe@214 286
jbe@286 287 -- define post-fork initialization function (including loading of "multirand" library)
jbe@286 288 local function postfork_init()
jbe@286 289 _G.multirand = require "multirand"
jbe@286 290 execute.postfork_initializers()
jbe@286 291 end
jbe@286 292
jbe@317 293 -- prepare for interactive or listen mode
jbe@203 294 if WEBMCP_MODE == "interactive" then
jbe@317 295 function listen() -- overwrite Moonbridge's listen function
jbe@317 296 -- ignore listen function calls for interactive mode
jbe@317 297 end
jbe@317 298 trace.disable() -- avoids memory leakage when scripts are running endlessly
jbe@317 299 else
jbe@317 300 local moonbridge_listen = listen
jbe@207 301 local http = require("moonbridge_http")
jbe@317 302 function listen(args) -- overwrite Moonbridge's listen function
jbe@317 303 local http_options = args.http_options or {}
jbe@317 304 local min_requests_per_fork = http_options.min_requests_per_fork or 50
jbe@317 305 local max_requests_per_fork = http_options.max_requests_per_fork or 100
jbe@316 306 local interval_handlers = {}
jbe@317 307 for j, listener in ipairs(args) do
jbe@317 308 if listener.proto == "interval" then
jbe@317 309 local name = listener.name or "Unnamed interval #" .. #interval_handlers+1
jbe@317 310 interval_handlers[name] = listener.handler
jbe@317 311 listener.name = name
jbe@316 312 end
jbe@316 313 end
jbe@264 314 local request_count = 0
jbe@266 315 local function inner_handler(http_request)
jbe@317 316 request.initialize()
jbe@288 317 request.handler(http_request, request_count >= max_requests_per_fork)
jbe@264 318 end
jbe@264 319 local outer_handler = http.generate_handler(inner_handler, http_options)
jbe@317 320 args.prepare = postfork_init
jbe@317 321 args.connect = function(socket)
jbe@316 322 request_count = request_count + 1
jbe@316 323 if socket.interval then
jbe@317 324 request.initialize()
jbe@316 325 interval_handlers[socket.interval]()
jbe@316 326 else
jbe@316 327 outer_handler(socket)
jbe@316 328 end
jbe@288 329 return request_count < min_requests_per_fork
jbe@264 330 end
jbe@317 331 args.finish = execute.finalizers
jbe@317 332 moonbridge_listen(args)
jbe@204 333 end
jbe@204 334 end
jbe@317 335
jbe@317 336 -- execute the __init.lua file in the environment's root
jbe@317 337 root_init()
jbe@317 338
jbe@317 339 -- prohibit (unintended) definition of new global variables
jbe@317 340 protect_globals()
jbe@317 341
jbe@317 342 -- execute configurations and pre-fork initializers
jbe@317 343 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
jbe@317 344 execute.config(config_name)
jbe@317 345 end
jbe@317 346 execute.prefork_initializers()
jbe@317 347
jbe@317 348 -- perform post-fork initializations in case of interactive mode
jbe@317 349 if WEBMCP_MODE == "interactive" then
jbe@317 350 postfork_init()
jbe@317 351 end

Impressum / About Us