#!/usr/bin/env moonbridge WEBMCP_VERSION = "2.0.0_devel" -- allow control of global environment local _G = _G local global_metatable = { __index = _G, __newindex = function(self, key, value) _G[key] = value end } _ENV = setmetatable({}, global_metatable) -- check if interactive mode if listen then -- defined by moonbridge WEBMCP_MODE = "listen" else WEBMCP_MODE = "interactive" end -- configuration names are provided as 4th, 5th, etc. argument WEBMCP_CONFIG_NAMES = {select(4, ...)} -- determine framework and bath path from command line arguments -- or print usage synopsis (if applicable) do local arg1, arg2, arg3 = ... local helpout if arg1 == "-h" or arg1 == "--help" or arg2 == "-h" or arg2 == "--help" -- if first arg is provided by wrapper then helpout = io.stdout elseif #WEBMCP_CONFIG_NAMES < 1 or (WEBMCP_MODE == "interactive") ~= (arg3 == "INTERACTIVE") then helpout = io.stderr end if helpout then helpout:write("Usage: moonbridge -- /bin/mcp.lua [ ...]\n") helpout:write(" or: lua -i /bin/mcp.lua INTERACTIVE [ ...]\n") if helpout == io.stderr then return 1 else return 0 end end local function append_trailing_slash(str) return string.gsub(str, "([^/])$", function(last) return last .. "/" end) end WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1) WEBMCP_BASE_PATH = append_trailing_slash(arg2) if WEBMCP_MODE == "listen" then WEBMCP_APP_NAME = arg3 end end -- setup search paths for libraries do if string.match(package.path, "^[^;]") then package.path = ";" .. package.path end package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path -- find out which file name extension shared libraries have local slib_exts = {} for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do if not slib_exts[ext] then slib_exts[#slib_exts+1] = ext slib_exts[ext] = true end end local paths = {} for i, ext in ipairs(slib_exts) do paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext end for i, ext in ipairs(slib_exts) do paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext end paths[#paths+1] = package.cpath package.cpath = table.concat(paths, ";") end -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/", -- application environment extensions "$WEBMCP_BASE_PATH/env/" -- and models "$WEBMCP_BASE_PATH/model/" do local weakkey_mt = { __mode = "k" } local autoloader_category = setmetatable({}, weakkey_mt) local autoloader_path = setmetatable({}, weakkey_mt) local autoloader_mt = {} local function install_autoloader(self, category, path_fragment) autoloader_category[self] = category autoloader_path[self] = path_fragment setmetatable(self, autoloader_mt) end local function try_exec(filename) local file = io.open(filename, "r") if file then local filedata = file:read("*a") io.close(file) local func, errmsg = load(filedata, "=" .. filename) if func then func() return true else error(errmsg, 0) end else return false end end function autoloader_mt.__index(self, key) local category, base_path, merge_base_path, file_key local merge = false if string.find(key, "^[a-z_][A-Za-z0-9_]*$") and not string.find(key, "^__") then category = "env" base_path = WEBMCP_FRAMEWORK_PATH .. "env/" merge = true merge_base_path = WEBMCP_BASE_PATH .. "env/" file_key = key elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then category = "model" base_path = WEBMCP_BASE_PATH .. "model/" local first = true file_key = string.gsub(key, "[A-Z]", function(c) if first then first = false return string.lower(c) else return "_" .. string.lower(c) end end ) else return end local required_category = autoloader_category[self] if required_category and required_category ~= category then return end local path_fragment = autoloader_path[self] local path = base_path .. path_fragment .. file_key local merge_path if merge then merge_path = merge_base_path .. path_fragment .. file_key end local function try_dir(dirname) local dir = io.open(dirname) if dir then io.close(dir) local obj = {} install_autoloader(obj, category, path_fragment .. file_key .. "/") rawset(self, key, obj) try_exec(path .. "/__init.lua") if merge then try_exec(merge_path .. "/__init.lua") end return true else return false end end if merge and try_exec(merge_path .. ".lua") then elseif merge and try_dir(merge_path .. "/") then elseif try_exec(path .. ".lua") then elseif try_dir(path .. "/") then else end return rawget(self, key) end install_autoloader(_G, nil, "") try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua") try_exec(WEBMCP_BASE_PATH .. "env/__init.lua") end -- replace Moonbridge listen function local moonbridge_listen = listen local listeners = {} function listen(args) listeners[#listeners+1] = args end -- prohibit (unintended) definition of new global variables function global_metatable.__newindex() error("Setting of global variable prohibited") end -- execute configurations and pre-fork initializers for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do execute.config(config_name) end execute.prefork_initializers() -- interactive console mode if WEBMCP_MODE == "interactive" then _G.multirand = require "multirand" -- TODO: cleaner solution execute.postfork_initializers() trace.disable() -- avoids memory leakage (TODO: needs general solution for moonbridge?) end -- invoke moonbridge if WEBMCP_MODE == "listen" then local http = require("moonbridge_http") for i, listener in ipairs(listeners) do --listener.prepare = execute.postfork_initializers listener.prepare = function() _G.multirand = require "multirand" execute.postfork_initializers() end listener.connect = http.generate_handler( request.handler, request.get_http_options() ) listener.finish = execute.finalizers moonbridge_listen(listener) end end