webmcp

annotate framework/bin/mcp.lua @ 313:d34b7b5e5e5c

Check if framework path is correct in mcp.lua (gives a more helpful error message if the framework path is wrong)
author jbe
date Sun Mar 22 23:08:18 2015 +0100 (2015-03-22)
parents 9fef75a02542
children a2c733535b8e
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@203 111 elseif
jbe@217 112 #WEBMCP_CONFIG_NAMES < 1 or
jbe@206 113 (WEBMCP_MODE == "interactive") ~= (arg3 == "INTERACTIVE")
jbe@203 114 then
jbe@203 115 helpout = io.stderr
jbe@203 116 end
jbe@203 117 if helpout then
jbe@217 118 helpout:write("Usage: moonbridge -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
jbe@217 119 helpout:write(" or: lua -i <framework path>/bin/mcp.lua <framework path> <app base path> INTERACTIVE <config name> [<config name> ...]\n")
jbe@203 120 if helpout == io.stderr then
jbe@203 121 return 1
jbe@68 122 else
jbe@203 123 return 0
jbe@68 124 end
jbe@68 125 end
jbe@203 126 local function append_trailing_slash(str)
jbe@217 127 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
jbe@203 128 end
jbe@203 129 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
jbe@203 130 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
jbe@206 131 if WEBMCP_MODE == "listen" then
jbe@206 132 WEBMCP_APP_NAME = arg3
jbe@206 133 end
jbe@68 134 end
jbe@1 135
jbe@313 136 -- check if framework path is correct
jbe@313 137 do
jbe@313 138 local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
jbe@313 139 if not file then
jbe@313 140 error('Could not find "webmcp_version" file: ' .. errmsg, 0)
jbe@313 141 end
jbe@313 142 local version = assert(file:read())
jbe@313 143 assert(file:close())
jbe@313 144 if version ~= WEBMCP_VERSION then
jbe@313 145 error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
jbe@313 146 end
jbe@313 147 end
jbe@313 148
jbe@203 149 -- setup search paths for libraries
jbe/bsw@0 150 do
jbe@217 151 if string.match(package.path, "^[^;]") then
jbe@217 152 package.path = ";" .. package.path
jbe@217 153 end
jbe@217 154 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
jbe/bsw@0 155 -- find out which file name extension shared libraries have
jbe/bsw@0 156 local slib_exts = {}
jbe/bsw@0 157 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
jbe@217 158 if not slib_exts[ext] then
jbe@217 159 slib_exts[#slib_exts+1] = ext
jbe@217 160 slib_exts[ext] = true
jbe@217 161 end
jbe/bsw@0 162 end
jbe/bsw@0 163 local paths = {}
jbe@217 164 for i, ext in ipairs(slib_exts) do
jbe@203 165 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
jbe/bsw@0 166 end
jbe@217 167 for i, ext in ipairs(slib_exts) do
jbe@203 168 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
jbe/bsw@0 169 end
jbe/bsw@0 170 paths[#paths+1] = package.cpath
jbe/bsw@0 171 package.cpath = table.concat(paths, ";")
jbe/bsw@0 172 end
jbe/bsw@0 173
jbe@203 174 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
jbe@203 175 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
jbe@203 176 -- and models "$WEBMCP_BASE_PATH/model/"
jbe/bsw@0 177 do
jbe/bsw@0 178 local weakkey_mt = { __mode = "k" }
jbe/bsw@0 179 local autoloader_category = setmetatable({}, weakkey_mt)
jbe/bsw@0 180 local autoloader_path = setmetatable({}, weakkey_mt)
jbe/bsw@0 181 local autoloader_mt = {}
jbe@219 182 local function install_autoloader(self, category, path_fragment)
jbe/bsw@0 183 autoloader_category[self] = category
jbe@219 184 autoloader_path[self] = path_fragment
jbe/bsw@0 185 setmetatable(self, autoloader_mt)
jbe/bsw@0 186 end
jbe/bsw@0 187 local function try_exec(filename)
jbe@309 188 local func = loadcached(filename)
jbe@309 189 if func then
jbe@309 190 func()
jbe@309 191 return true
jbe/bsw@0 192 else
jbe/bsw@0 193 return false
jbe/bsw@0 194 end
jbe/bsw@0 195 end
jbe/bsw@0 196 function autoloader_mt.__index(self, key)
jbe/bsw@0 197 local category, base_path, merge_base_path, file_key
jbe/bsw@0 198 local merge = false
jbe/bsw@0 199 if
jbe/bsw@0 200 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
jbe/bsw@0 201 not string.find(key, "^__")
jbe/bsw@0 202 then
jbe/bsw@0 203 category = "env"
jbe@203 204 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
jbe/bsw@0 205 merge = true
jbe@203 206 merge_base_path = WEBMCP_BASE_PATH .. "env/"
jbe/bsw@0 207 file_key = key
jbe/bsw@0 208 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
jbe/bsw@0 209 category = "model"
jbe@203 210 base_path = WEBMCP_BASE_PATH .. "model/"
jbe/bsw@0 211 local first = true
jbe/bsw@0 212 file_key = string.gsub(key, "[A-Z]",
jbe/bsw@0 213 function(c)
jbe/bsw@0 214 if first then
jbe/bsw@0 215 first = false
jbe/bsw@0 216 return string.lower(c)
jbe/bsw@0 217 else
jbe/bsw@0 218 return "_" .. string.lower(c)
jbe/bsw@0 219 end
jbe/bsw@0 220 end
jbe/bsw@0 221 )
jbe/bsw@0 222 else
jbe/bsw@0 223 return
jbe/bsw@0 224 end
jbe/bsw@0 225 local required_category = autoloader_category[self]
jbe/bsw@0 226 if required_category and required_category ~= category then return end
jbe@219 227 local path_fragment = autoloader_path[self]
jbe@219 228 local path = base_path .. path_fragment .. file_key
jbe@219 229 local merge_path
jbe/bsw@0 230 if merge then
jbe@219 231 merge_path = merge_base_path .. path_fragment .. file_key
jbe/bsw@0 232 end
jbe/bsw@0 233 local function try_dir(dirname)
jbe/bsw@0 234 local dir = io.open(dirname)
jbe/bsw@0 235 if dir then
jbe/bsw@0 236 io.close(dir)
jbe/bsw@0 237 local obj = {}
jbe@219 238 install_autoloader(obj, category, path_fragment .. file_key .. "/")
jbe/bsw@0 239 rawset(self, key, obj)
jbe@219 240 try_exec(path .. "/__init.lua")
jbe@219 241 if merge then try_exec(merge_path .. "/__init.lua") end
jbe/bsw@0 242 return true
jbe/bsw@0 243 else
jbe/bsw@0 244 return false
jbe/bsw@0 245 end
jbe/bsw@0 246 end
jbe@238 247 if self == _G then
jbe@237 248 allowed_globals[key] = true
jbe@233 249 end
jbe@219 250 if merge and try_exec(merge_path .. ".lua") then
jbe@219 251 elseif merge and try_dir(merge_path .. "/") then
jbe@219 252 elseif try_exec(path .. ".lua") then
jbe@219 253 elseif try_dir(path .. "/") then
jbe/bsw@0 254 else end
jbe@238 255 if self == _G then
jbe@237 256 allowed_globals[key] = nil
jbe@233 257 end
jbe@237 258 return rawget(self, key)
jbe/bsw@0 259 end
jbe@219 260 install_autoloader(_G, nil, "")
jbe@203 261 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
jbe@203 262 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
jbe/bsw@0 263 end
jbe/bsw@0 264
jbe@214 265 -- replace Moonbridge listen function
jbe@214 266 local moonbridge_listen = listen
jbe@225 267 local listeners = {}
jbe@214 268 function listen(args)
jbe@214 269 listeners[#listeners+1] = args
jbe@214 270 end
jbe@214 271
jbe@203 272 -- prohibit (unintended) definition of new global variables
jbe@237 273 function global_metatable.__newindex(self, key, value)
jbe@237 274 if not allowed_globals[key] then
jbe@237 275 error("Setting of global variable prohibited", 2)
jbe@237 276 end
jbe@239 277 _G[key] = value
jbe@231 278 end
jbe@203 279
jbe@220 280 -- execute configurations and pre-fork initializers
jbe@206 281 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
jbe@206 282 execute.config(config_name)
jbe@206 283 end
jbe@220 284 execute.prefork_initializers()
jbe@206 285
jbe@286 286 -- define post-fork initialization function (including loading of "multirand" library)
jbe@286 287 local function postfork_init()
jbe@286 288 _G.multirand = require "multirand"
jbe@286 289 execute.postfork_initializers()
jbe@286 290 end
jbe@286 291
jbe/bsw@0 292 -- interactive console mode
jbe@203 293 if WEBMCP_MODE == "interactive" then
jbe@286 294 postfork_init()
jbe@289 295 trace.disable() -- avoids memory leakage
jbe/bsw@0 296 end
jbe/bsw@0 297
jbe@204 298 -- invoke moonbridge
jbe@206 299 if WEBMCP_MODE == "listen" then
jbe@264 300 local http_options = request.get_http_options()
jbe@288 301 local min_requests_per_fork = http_options.min_requests_per_fork or 50
jbe@288 302 local max_requests_per_fork = http_options.max_requests_per_fork or 100
jbe@207 303 local http = require("moonbridge_http")
jbe@211 304 for i, listener in ipairs(listeners) do
jbe@264 305 local request_count = 0
jbe@266 306 local function inner_handler(http_request)
jbe@264 307 request_count = request_count + 1
jbe@288 308 request.handler(http_request, request_count >= max_requests_per_fork)
jbe@264 309 end
jbe@264 310 local outer_handler = http.generate_handler(inner_handler, http_options)
jbe@286 311 listener.prepare = postfork_init
jbe@264 312 listener.connect = function(socket)
jbe@264 313 outer_handler(socket)
jbe@288 314 return request_count < min_requests_per_fork
jbe@264 315 end
jbe@211 316 listener.finish = execute.finalizers
jbe@211 317 moonbridge_listen(listener)
jbe@204 318 end
jbe@204 319 end
jbe@204 320

Impressum / About Us