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