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