webmcp

annotate framework/bin/mcp.lua @ 278:b5d0c0ab3ce2

Updated documentation; Switched version string to "2.0.0"
author jbe
date Sat Mar 21 18:40:44 2015 +0100 (2015-03-21)
parents 98537ccd0800
children fc2aba7d5db9
rev   line source
jbe@203 1 #!/usr/bin/env moonbridge
jbe/bsw@0 2
jbe@278 3 WEBMCP_VERSION = "2.0.0"
jbe@64 4
jbe@231 5 -- allow control of global environment
jbe@231 6 local _G = _G
jbe@237 7 local allowed_globals = {}
jbe@231 8 local global_metatable = {
jbe@231 9 __index = _G,
jbe@231 10 __newindex = function(self, key, value)
jbe@231 11 _G[key] = value
jbe@231 12 end
jbe@231 13 }
jbe@231 14 _ENV = setmetatable({}, global_metatable)
jbe@231 15
jbe@203 16 -- check if interactive mode
jbe@203 17 if listen then -- defined by moonbridge
jbe@203 18 WEBMCP_MODE = "listen"
jbe@203 19 else
jbe@203 20 WEBMCP_MODE = "interactive"
jbe@64 21 end
jbe@203 22
jbe@206 23 -- configuration names are provided as 4th, 5th, etc. argument
jbe@206 24 WEBMCP_CONFIG_NAMES = {select(4, ...)}
jbe@203 25
jbe@203 26 -- determine framework and bath path from command line arguments
jbe@203 27 -- or print usage synopsis (if applicable)
jbe@68 28 do
jbe@206 29 local arg1, arg2, arg3 = ...
jbe@203 30 local helpout
jbe@203 31 if
jbe@203 32 arg1 == "-h" or arg1 == "--help" or
jbe@206 33 arg2 == "-h" or arg2 == "--help" -- if first arg is provided by wrapper
jbe@203 34 then
jbe@203 35 helpout = io.stdout
jbe@203 36 elseif
jbe@217 37 #WEBMCP_CONFIG_NAMES < 1 or
jbe@206 38 (WEBMCP_MODE == "interactive") ~= (arg3 == "INTERACTIVE")
jbe@203 39 then
jbe@203 40 helpout = io.stderr
jbe@203 41 end
jbe@203 42 if helpout then
jbe@217 43 helpout:write("Usage: moonbridge -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
jbe@217 44 helpout:write(" or: lua -i <framework path>/bin/mcp.lua <framework path> <app base path> INTERACTIVE <config name> [<config name> ...]\n")
jbe@203 45 if helpout == io.stderr then
jbe@203 46 return 1
jbe@68 47 else
jbe@203 48 return 0
jbe@68 49 end
jbe@68 50 end
jbe@203 51 local function append_trailing_slash(str)
jbe@217 52 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
jbe@203 53 end
jbe@203 54 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
jbe@203 55 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
jbe@206 56 if WEBMCP_MODE == "listen" then
jbe@206 57 WEBMCP_APP_NAME = arg3
jbe@206 58 end
jbe@68 59 end
jbe@1 60
jbe@203 61 -- setup search paths for libraries
jbe/bsw@0 62 do
jbe@217 63 if string.match(package.path, "^[^;]") then
jbe@217 64 package.path = ";" .. package.path
jbe@217 65 end
jbe@217 66 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
jbe/bsw@0 67 -- find out which file name extension shared libraries have
jbe/bsw@0 68 local slib_exts = {}
jbe/bsw@0 69 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
jbe@217 70 if not slib_exts[ext] then
jbe@217 71 slib_exts[#slib_exts+1] = ext
jbe@217 72 slib_exts[ext] = true
jbe@217 73 end
jbe/bsw@0 74 end
jbe/bsw@0 75 local paths = {}
jbe@217 76 for i, ext in ipairs(slib_exts) do
jbe@203 77 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
jbe/bsw@0 78 end
jbe@217 79 for i, ext in ipairs(slib_exts) do
jbe@203 80 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
jbe/bsw@0 81 end
jbe/bsw@0 82 paths[#paths+1] = package.cpath
jbe/bsw@0 83 package.cpath = table.concat(paths, ";")
jbe/bsw@0 84 end
jbe/bsw@0 85
jbe@203 86 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
jbe@203 87 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
jbe@203 88 -- and models "$WEBMCP_BASE_PATH/model/"
jbe/bsw@0 89 do
jbe/bsw@0 90 local weakkey_mt = { __mode = "k" }
jbe/bsw@0 91 local autoloader_category = setmetatable({}, weakkey_mt)
jbe/bsw@0 92 local autoloader_path = setmetatable({}, weakkey_mt)
jbe/bsw@0 93 local autoloader_mt = {}
jbe@219 94 local function install_autoloader(self, category, path_fragment)
jbe/bsw@0 95 autoloader_category[self] = category
jbe@219 96 autoloader_path[self] = path_fragment
jbe/bsw@0 97 setmetatable(self, autoloader_mt)
jbe/bsw@0 98 end
jbe/bsw@0 99 local function try_exec(filename)
jbe/bsw@0 100 local file = io.open(filename, "r")
jbe/bsw@0 101 if file then
jbe/bsw@0 102 local filedata = file:read("*a")
jbe/bsw@0 103 io.close(file)
jbe@232 104 local func, errmsg = load(filedata, "=" .. filename, nil, _ENV)
jbe/bsw@0 105 if func then
jbe/bsw@0 106 func()
jbe/bsw@0 107 return true
jbe/bsw@0 108 else
jbe/bsw@0 109 error(errmsg, 0)
jbe/bsw@0 110 end
jbe/bsw@0 111 else
jbe/bsw@0 112 return false
jbe/bsw@0 113 end
jbe/bsw@0 114 end
jbe/bsw@0 115 function autoloader_mt.__index(self, key)
jbe/bsw@0 116 local category, base_path, merge_base_path, file_key
jbe/bsw@0 117 local merge = false
jbe/bsw@0 118 if
jbe/bsw@0 119 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
jbe/bsw@0 120 not string.find(key, "^__")
jbe/bsw@0 121 then
jbe/bsw@0 122 category = "env"
jbe@203 123 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
jbe/bsw@0 124 merge = true
jbe@203 125 merge_base_path = WEBMCP_BASE_PATH .. "env/"
jbe/bsw@0 126 file_key = key
jbe/bsw@0 127 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
jbe/bsw@0 128 category = "model"
jbe@203 129 base_path = WEBMCP_BASE_PATH .. "model/"
jbe/bsw@0 130 local first = true
jbe/bsw@0 131 file_key = string.gsub(key, "[A-Z]",
jbe/bsw@0 132 function(c)
jbe/bsw@0 133 if first then
jbe/bsw@0 134 first = false
jbe/bsw@0 135 return string.lower(c)
jbe/bsw@0 136 else
jbe/bsw@0 137 return "_" .. string.lower(c)
jbe/bsw@0 138 end
jbe/bsw@0 139 end
jbe/bsw@0 140 )
jbe/bsw@0 141 else
jbe/bsw@0 142 return
jbe/bsw@0 143 end
jbe/bsw@0 144 local required_category = autoloader_category[self]
jbe/bsw@0 145 if required_category and required_category ~= category then return end
jbe@219 146 local path_fragment = autoloader_path[self]
jbe@219 147 local path = base_path .. path_fragment .. file_key
jbe@219 148 local merge_path
jbe/bsw@0 149 if merge then
jbe@219 150 merge_path = merge_base_path .. path_fragment .. file_key
jbe/bsw@0 151 end
jbe/bsw@0 152 local function try_dir(dirname)
jbe/bsw@0 153 local dir = io.open(dirname)
jbe/bsw@0 154 if dir then
jbe/bsw@0 155 io.close(dir)
jbe/bsw@0 156 local obj = {}
jbe@219 157 install_autoloader(obj, category, path_fragment .. file_key .. "/")
jbe/bsw@0 158 rawset(self, key, obj)
jbe@219 159 try_exec(path .. "/__init.lua")
jbe@219 160 if merge then try_exec(merge_path .. "/__init.lua") end
jbe/bsw@0 161 return true
jbe/bsw@0 162 else
jbe/bsw@0 163 return false
jbe/bsw@0 164 end
jbe/bsw@0 165 end
jbe@238 166 if self == _G then
jbe@237 167 allowed_globals[key] = true
jbe@233 168 end
jbe@219 169 if merge and try_exec(merge_path .. ".lua") then
jbe@219 170 elseif merge and try_dir(merge_path .. "/") then
jbe@219 171 elseif try_exec(path .. ".lua") then
jbe@219 172 elseif try_dir(path .. "/") then
jbe/bsw@0 173 else end
jbe@238 174 if self == _G then
jbe@237 175 allowed_globals[key] = nil
jbe@233 176 end
jbe@237 177 return rawget(self, key)
jbe/bsw@0 178 end
jbe@219 179 install_autoloader(_G, nil, "")
jbe@203 180 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
jbe@203 181 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
jbe/bsw@0 182 end
jbe/bsw@0 183
jbe@214 184 -- replace Moonbridge listen function
jbe@214 185 local moonbridge_listen = listen
jbe@225 186 local listeners = {}
jbe@214 187 function listen(args)
jbe@214 188 listeners[#listeners+1] = args
jbe@214 189 end
jbe@214 190
jbe@203 191 -- prohibit (unintended) definition of new global variables
jbe@237 192 function global_metatable.__newindex(self, key, value)
jbe@237 193 if not allowed_globals[key] then
jbe@237 194 error("Setting of global variable prohibited", 2)
jbe@237 195 end
jbe@239 196 _G[key] = value
jbe@231 197 end
jbe@203 198
jbe@220 199 -- execute configurations and pre-fork initializers
jbe@206 200 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
jbe@206 201 execute.config(config_name)
jbe@206 202 end
jbe@220 203 execute.prefork_initializers()
jbe@206 204
jbe/bsw@0 205 -- interactive console mode
jbe@203 206 if WEBMCP_MODE == "interactive" then
jbe@229 207 _G.multirand = require "multirand" -- TODO: cleaner solution
jbe@215 208 execute.postfork_initializers()
jbe@203 209 trace.disable() -- avoids memory leakage (TODO: needs general solution for moonbridge?)
jbe/bsw@0 210 end
jbe/bsw@0 211
jbe@204 212 -- invoke moonbridge
jbe@206 213 if WEBMCP_MODE == "listen" then
jbe@264 214 local http_options = request.get_http_options()
jbe@264 215 local min_requests_per_connect = http_options.min_requests_per_connect or 90
jbe@264 216 local max_requests_per_connect = http_options.max_requests_per_connect or 100
jbe@207 217 local http = require("moonbridge_http")
jbe@211 218 for i, listener in ipairs(listeners) do
jbe@264 219 local request_count = 0
jbe@266 220 local function inner_handler(http_request)
jbe@264 221 request_count = request_count + 1
jbe@266 222 request.handler(http_request, request_count >= max_requests_per_connect)
jbe@264 223 end
jbe@264 224 local outer_handler = http.generate_handler(inner_handler, http_options)
jbe@229 225 --listener.prepare = execute.postfork_initializers
jbe@229 226 listener.prepare = function()
jbe@229 227 _G.multirand = require "multirand"
jbe@229 228 execute.postfork_initializers()
jbe@229 229 end
jbe@264 230 listener.connect = function(socket)
jbe@264 231 outer_handler(socket)
jbe@264 232 return request_count < min_requests_per_connect
jbe@264 233 end
jbe@211 234 listener.finish = execute.finalizers
jbe@211 235 moonbridge_listen(listener)
jbe@204 236 end
jbe@204 237 end
jbe@204 238

Impressum / About Us