webmcp

annotate framework/bin/mcp.lua @ 316:a2c733535b8e

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

Impressum / About Us