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