webmcp

annotate framework/bin/mcp.lua @ 323:4603e8781fb9

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

Impressum / About Us