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