webmcp
view framework/bin/mcp.lua @ 318:f5660406ad3b
Added missing autodoc end tag in mcp.lua
| author | jbe | 
|---|---|
| date | Mon Mar 23 19:09:12 2015 +0100 (2015-03-23) | 
| parents | 732c4d53a823 | 
| children | 6bb85c1bf11a | 
 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 --[[--
    12 _G
    14 A reference to the global namespace. To avoid accidental programming errors, global variables cannot be set directly, but they must be set through the _G reference, e.g. use _G.foo = true to set the variable "foo" to a value of true.
    16 The only exception is the <framework>/env/__init.lua file and the <application>/env/__init.lua file, in which global variables may be set without using the _G reference.
    18 Note that the global namespace may or may not be shared between requests (Moonbridge creates multiple forks of the Lua machine). To set variables that are to be cleared after the request has been finished, an application may use the "app" table, e.g. app.foo = true to set the variable app.foo to a value of true, which will be cleared automatically when the request has ended.
    20 --]]--
    21 local _G = _G
    22 local allowed_globals = {}
    23 local global_metatable = {
    24   __index = _G,
    25   __newindex = function(self, key, value)
    26     _G[key] = value
    27   end
    28 }
    29 _ENV = setmetatable(
    30   {},  -- proxy environment used within mcp.lua and by all chunks loaded through loadcached(...)
    31   global_metatable
    32 ) 
    33 local function protect_globals()  -- called before first configuration file is loaded
    34   function global_metatable.__newindex(self, key, value)
    35     if allowed_globals[key] then
    36       _G[key] = value
    37     else
    38       if type(key) == "string" and string.match(key, "^[A-Za-z_][A-Za-z_0-9]*$") then
    39         error('Attempt to set global variable "' .. key .. '" (hint: use _G.' .. key .. '=<value> to override protection mechnamism)', 2)
    40       else
    41         error('Attempt to set global variable', 2)
    42       end
    43     end
    44   end
    45 end
    46 --//--
    48 --[[--
    49 lua_func =   -- compiled Lua function
    50 loadcached(
    51   filename   -- path to a Lua source or byte-code file
    52 )
    54 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.
    56 --]]--
    57 do
    58   local cache = {}
    59   function loadcached(filename)
    60     local cached_func = cache[filename]
    61     if cached_func then
    62       return cached_func
    63     end
    64     local file, read_error = io.open(filename, "r")
    65     if file then
    66       local filedata = assert(file:read("*a"))
    67       assert(file:close())
    68       local func, compile_error = load(filedata, "=" .. filename, nil, _ENV)
    69       if func then
    70         cache[filename] = func
    71         return func
    72       end
    73       error(compile_error, 0)
    74     else
    75       return nil, read_error
    76     end
    77   end
    78 end
    79 --//--
    81 --[[--
    82 WEBMCP_MODE
    84 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode.
    85 --]]--
    86 if listen then  -- defined by moonbridge
    87   WEBMCP_MODE = "listen"
    88 else
    89   WEBMCP_MODE = "interactive"
    90 end
    91 --//--
    93 --[[--
    94 WEBMCP_CONFIG_NAMES
    96 A list of the selected configuration names.
    97 --]]--
    98 -- configuration names are provided as 4th, 5th, etc. command line argument
    99 WEBMCP_CONFIG_NAMES = {select(4, ...)}
   100 --//--
   102 --[[--
   103 WEBMCP_FRAMEWORK_PATH
   105 Directory of the WebMCP framework (always includes a trailing slash).
   106 --]]--
   107 -- set in mcp.lua
   108 --//--
   110 --[[--
   111 WEBMCP_BASE_PATH
   113 Base directory of the application (always includes a trailing slash).
   114 --]]--
   115 -- set in mcp.lua
   116 --//--
   118 --[[--
   119 WEBMCP_APP_NAME
   121 Application name (usually "main"). May be nil in case of interactive mode.
   122 --]]--
   123 -- set in mcp.lua
   124 --//--
   126 -- determine framework and bath path from command line arguments
   127 -- or print usage synopsis (if applicable)
   128 do
   129   local arg1, arg2, arg3 = ...
   130   local helpout
   131   if
   132     arg1 == "-h" or arg1 == "--help" or
   133     arg2 == "-h" or arg2 == "--help"  -- if first arg is provided by wrapper
   134   then
   135     helpout = io.stdout
   136   elseif #WEBMCP_CONFIG_NAMES < 1 then
   137     helpout = io.stderr
   138   end
   139   if helpout then
   140     helpout:write("Usage: moonbridge [moonbr opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
   141     helpout:write("   or: lua -i     [Lua opts]    -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
   142     if helpout == io.stderr then
   143       return 1
   144     else
   145       return 0
   146     end
   147   end
   148   local function append_trailing_slash(str)
   149     return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
   150   end
   151   WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
   152   WEBMCP_BASE_PATH      = append_trailing_slash(arg2)
   153   WEBMCP_APP_NAME       = arg3
   154 end
   156 -- check if framework path is correct
   157 do
   158   local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
   159   if not file then
   160     error('Could not find "webmcp_version" file: ' .. errmsg, 0)
   161   end
   162   local version = assert(file:read())
   163   assert(file:close())
   164   if version ~= WEBMCP_VERSION then
   165     error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
   166   end
   167 end
   169 -- setup search paths for libraries
   170 do
   171   if string.match(package.path, "^[^;]") then
   172     package.path = ";" .. package.path
   173   end
   174   package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
   175   -- find out which file name extension shared libraries have
   176   local slib_exts = {}
   177   for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
   178     if not slib_exts[ext] then
   179       slib_exts[#slib_exts+1] = ext
   180       slib_exts[ext] = true
   181     end
   182   end
   183   local paths = {}
   184   for i, ext in ipairs(slib_exts) do
   185     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
   186   end
   187   for i, ext in ipairs(slib_exts) do
   188     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
   189   end
   190   paths[#paths+1] = package.cpath
   191   package.cpath = table.concat(paths, ";")
   192 end
   194 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
   195 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
   196 -- and models "$WEBMCP_BASE_PATH/model/"
   197 local root_init  -- function which executes the __init.lua file in the environment's root
   198 do
   199   local weakkey_mt = { __mode = "k" }
   200   local autoloader_category = setmetatable({}, weakkey_mt)
   201   local autoloader_path     = setmetatable({}, weakkey_mt)
   202   local autoloader_mt       = {}
   203   local function install_autoloader(self, category, path_fragment)
   204     autoloader_category[self] = category
   205     autoloader_path[self]     = path_fragment
   206     setmetatable(self, autoloader_mt)
   207   end
   208   local function try_exec(filename)
   209     local func = loadcached(filename)
   210     if func then
   211       func()
   212       return true
   213     else
   214       return false
   215     end
   216   end
   217   function autoloader_mt.__index(self, key)
   218     local category, base_path, merge_base_path, file_key
   219     local merge = false
   220     if
   221       string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   222       not string.find(key, "^__")
   223     then
   224       category        = "env"
   225       base_path       = WEBMCP_FRAMEWORK_PATH .. "env/"
   226       merge           = true
   227       merge_base_path = WEBMCP_BASE_PATH .. "env/"
   228       file_key        = key
   229     elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   230       category        = "model"
   231       base_path       = WEBMCP_BASE_PATH .. "model/"
   232       local first = true
   233       file_key = string.gsub(key, "[A-Z]",
   234         function(c)
   235           if first then
   236             first = false
   237             return string.lower(c)
   238           else
   239             return "_" .. string.lower(c)
   240           end
   241         end
   242       )
   243     else
   244       return
   245     end
   246     local required_category = autoloader_category[self]
   247     if required_category and required_category ~= category then return end
   248     local path_fragment = autoloader_path[self]
   249     local path = base_path .. path_fragment .. file_key
   250     local merge_path
   251     if merge then
   252       merge_path = merge_base_path .. path_fragment .. file_key
   253     end
   254     local function try_dir(dirname)
   255       local dir = io.open(dirname)
   256       if dir then
   257         io.close(dir)
   258         local obj = {}
   259         install_autoloader(obj, category, path_fragment .. file_key .. "/")
   260         rawset(self, key, obj)
   261         try_exec(path .. "/__init.lua")
   262         if merge then try_exec(merge_path .. "/__init.lua") end
   263         return true
   264       else
   265         return false
   266       end
   267     end
   268     if self == _G then
   269       allowed_globals[key] = true
   270     end
   271     if merge and try_exec(merge_path .. ".lua") then
   272     elseif merge and try_dir(merge_path .. "/") then
   273     elseif try_exec(path .. ".lua") then
   274     elseif try_dir(path .. "/") then
   275     else end
   276     if self == _G then
   277       allowed_globals[key] = nil
   278     end
   279     return rawget(self, key)
   280   end
   281   install_autoloader(_G, nil, "")
   282   function root_init()  -- upvalue
   283     try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
   284     try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
   285   end
   286 end
   288 -- define post-fork initialization function (including loading of "multirand" library)
   289 local function postfork_init()
   290   _G.multirand = require "multirand"
   291   execute.postfork_initializers()
   292 end
   294 -- prepare for interactive or listen mode
   295 if WEBMCP_MODE == "interactive" then
   296   function listen()  -- overwrite Moonbridge's listen function
   297     -- ignore listen function calls for interactive mode
   298   end
   299   trace.disable()  -- avoids memory leakage when scripts are running endlessly
   300 else
   301   local moonbridge_listen = listen
   302   local http = require("moonbridge_http")
   303   function listen(args)  -- overwrite Moonbridge's listen function
   304     local http_options = args.http_options or {}
   305     local min_requests_per_fork = http_options.min_requests_per_fork or 50
   306     local max_requests_per_fork = http_options.max_requests_per_fork or 100
   307     local interval_handlers = {}
   308     for j, listener in ipairs(args) do
   309       if listener.proto == "interval" then
   310         local name = listener.name or "Unnamed interval #" .. #interval_handlers+1
   311         interval_handlers[name] = listener.handler
   312         listener.name = name
   313       end
   314     end
   315     local request_count = 0
   316     local function inner_handler(http_request)
   317       request.initialize()
   318       request.handler(http_request, request_count >= max_requests_per_fork)
   319     end
   320     local outer_handler = http.generate_handler(inner_handler, http_options)
   321     args.prepare = postfork_init
   322     args.connect = function(socket)
   323       request_count = request_count + 1
   324       if socket.interval then
   325         request.initialize()
   326         interval_handlers[socket.interval]()
   327       else
   328         outer_handler(socket)
   329       end
   330       return request_count < min_requests_per_fork
   331     end
   332     args.finish = execute.finalizers
   333     moonbridge_listen(args)
   334   end
   335 end
   337 -- execute the __init.lua file in the environment's root
   338 root_init()
   340 -- prohibit (unintended) definition of new global variables
   341 protect_globals()
   343 -- execute configurations and pre-fork initializers
   344 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
   345   execute.config(config_name)
   346 end
   347 execute.prefork_initializers()
   349 -- perform post-fork initializations in case of interactive mode
   350 if WEBMCP_MODE == "interactive" then
   351   postfork_init()
   352 end
